UOJ348 WC2018 州区划分

Problem

UOJ

Solution

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

h [ s ] = ∑ x ∈ s w x h[s]=\sum_{x\in s} w_x h[s]=xswx,如果 s s s是合法的那么 g [ s ] = h [ s ] g[s]=h[s] g[s]=h[s],否则 g [ s ] = 0 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] f[s]=h[s]k1tsg[t]k×f[st]

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

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

Code

#include 
#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;
}

你可能感兴趣的:(好题集,多项式,BZOJ,UOJ)