UOJ348 WC2018 州区划分

版权声明:写得不好,转载请通知一声,还请注明出处,感激不尽 https://blog.csdn.net/As_A_Kid/article/details/86623613

Problem

UOJ

Solution

做的时候SB了,纠结了好久怎么判定欧拉回路,YY了半天状压DP无果,后来突然想起欧拉回路的充要条件是联通且点的度数为偶数。

h [ s ] = x s w x h[s]=\sum_{x\in s} w_x ,如果 s s 是合法的那么 g [ s ] = h [ s ] g[s]=h[s] ,否则 g [ s ] = 0 g[s]=0

那么枚举最后的划分出来的子集

f [ s ] = 1 h [ s ] k t s g [ t ] k × f [ s t ] f[s]=\frac 1 {h[s]^k}\sum_{t\subseteq s} g[t]^k \times f[s-t]

这个就是一个子集卷积。子集卷积有一个 O ( n 2 2 n ) O(n^22^n) 的方法,就是按元素个数从小到大计算 f [ S ] f[S] 。在计算 f [ S ] f[S] 的时候,使有 j j 个元素的 g g 和有 i j i-j 个元素的 f f 做一次或卷积,为了限定两个集合无交集,卷积后如果元素个数小于 i i 个,我们就去掉它的贡献。

时间复杂度 O ( n 2 2 n ) O(n^22^n) ,空间复杂度 O ( n 2 n ) O(n2^n)

Code

#include <cstdio>
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long ll;
const int maxn=2100000,mod=998244353;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
int n,m,p,N,w[25],e[25][25],fa[25],cnt[maxn],lg[maxn];
int f[22][maxn],g[22][maxn],ig[maxn];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int merge(int x,int y)
{
	int fx=find(x),fy=find(y);
	if(fx==fy) return 0;
	return fa[fy]=fx;
}
int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
int power(int x,int y)
{
	int res=1;
	for(;y;y>>=1,x=(ll)x*x%mod)
	  if(y&1)
	    res=(ll)res*x%mod;
	return res;
}
int check(int s)
{
	static int top,tot,stk[25],d[25];
	if(cnt[s]==1) return 0;
	top=0;
	for(;s;s-=lowbit(s)) stk[++top]=lg[lowbit(s)],d[top]=0,fa[top]=top;
	tot=top;
	for(int i=1;i<=top;i++)
	  for(int j=1;j<i;j++)
	    if(e[stk[i]][stk[j]])
	    {
	    	++d[i],++d[j];
	    	if(merge(i,j)) tot--;
	    }
	if(tot>1) return 1;
	for(int i=1;i<=top;i++) if(d[i]&1) return 1;
	return 0;
}
void input()
{
	int u,v;
	read(n);read(m);read(p);N=1<<n;
	for(int i=1;i<=m;i++){read(u);read(v);e[u][v]=e[v][u]=1;}
	for(int i=1;i<=n;i++) read(w[i]),lg[1<<i-1]=i,g[1][1<<i-1]=w[i];
	for(int i=1;i<N;i++)
	{
		cnt[i]=cnt[i>>1]+(i&1);
		if(cnt[i]>1) g[cnt[i]][i]=g[cnt[i]-1][i-lowbit(i)]+w[lg[lowbit(i)]];
	}
	for(int i=1;i<N;i++)
	{
		g[cnt[i]][i]=power(g[cnt[i]][i],p);
		if(g[cnt[i]][i]) ig[i]=power(g[cnt[i]][i],mod-2);
		if(!check(i)) g[cnt[i]][i]=0;
	}
}
void FWT_or(int *a,int f)
{
	for(int i=1;i<N;i<<=1)
	  for(int j=0;j<N;j+=(i<<1))
	    for(int k=0;k<i;k++)
	      a[i+j+k]=((f==1)?pls(a[i+j+k],a[j+k]):dec(a[i+j+k],a[j+k]));
}
void work()
{
	for(int i=1;i<=n;i++) FWT_or(g[i],1);
	f[0][0]=1;FWT_or(f[0],1);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		  for(int r=0;r<N;r++)
		    f[i][r]=pls(f[i][r],(ll)g[j][r]*f[i-j][r]%mod);
		FWT_or(f[i],-1);
		for(int r=0;r<N;r++) f[i][r]=(cnt[r]==i?(ll)f[i][r]*ig[r]%mod:0);
		if(i^n) FWT_or(f[i],1);
	}
	printf("%d\n",f[n][N-1]);
}
int main()
{
	input();
	work();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/As_A_Kid/article/details/86623613