[WC2018]州区划分(状压DP+FWT/FMT)
很裸的子集反演模板题,套上一些莫名其妙的外衣。
先预处理每个集合是否合法,再作显然的状压DP。然后发现可以写成子集反演的形式,直接套模板即可。
子集反演可以看这里。
子集反演的过程就是多设一维代表集合大小,再FMT处理集合并卷积。
然而我的FMT常数过大,而并卷积又可以用FWT实现,于是就写FWT了。(实际上就三行的区别)
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 using namespace std; 6 7 const int N=22,mod=998244353; 8 int n,m,p,x,y,mp[N][N],w[N],fa[N],a[N],d[N],sm[1<<N],sz[1<<N],lg[1<<N],f[N][1<<N],g[N][1<<N]; 9 10 int pow(int a,int b){ return b==0 ? 1 : (b==1 ? a : 1ll*a*a%mod); } 11 int get(int x){ return (fa[x]==x) ? x : fa[x]=get(fa[x]); } 12 13 int ksm(int a,int b){ 14 int res=1; 15 for (; b; a=1ll*a*a%mod,b>>=1) 16 if (b & 1) res=1ll*res*a%mod; 17 return res; 18 } 19 20 void init(){ 21 rep(i,0,n) lg[1<<i]=i; 22 rep(S,1,(1<<n)-1){ 23 int t=S&-S,tot=0; 24 sm[S]=sm[S^t]+w[lg[t]]; sz[S]=sz[S^t]+1; 25 for (int i=S; i; i=i^(i&-i)) a[++tot]=lg[i&-i]; 26 rep(i,1,tot) fa[a[i]]=a[i],d[a[i]]=0; 27 rep(i,1,tot){ 28 rep(j,1,tot) if (mp[a[i]][a[j]]) d[a[i]]^=1,fa[get(a[i])]=get(a[j]); 29 if (d[a[i]]) { g[sz[S]][S]=1; break; } 30 } 31 int f=get(a[1]); 32 rep(i,2,tot) if (f!=get(a[i])){ g[sz[S]][S]=1; break; } 33 } 34 rep(i,0,(1<<n)-1) sm[i]=pow(sm[i],p),g[sz[i]][i]=1ll*g[sz[i]][i]*sm[i]%mod; 35 } 36 37 void FMT(int a[],int n,int f){ 38 for (int i=1; i<n; i<<=1) 39 rep(j,0,n-1) if (j&i) a[j]=(a[j]+1ll*f*a[j^i]+mod)%mod; 40 } 41 42 void solve(){ 43 f[0][0]=1; FMT(f[0],1<<n,1); 44 rep(i,0,n) FMT(g[i],1<<n,1); 45 rep(i,1,n){ 46 rep(j,0,(1<<n)-1) rep(x,0,i-1) 47 f[i][j]=(f[i][j]+1ll*f[x][j]*g[i-x][j])%mod; 48 FMT(f[i],1<<n,-1); 49 rep(j,0,(1<<n)-1) 50 if (sz[j]==i) f[i][j]=1ll*f[i][j]*ksm(sm[j],mod-2)%mod; else f[i][j]=0; 51 FMT(f[i],1<<n,1); 52 } 53 FMT(f[n],1<<n,-1); printf("%d\n",f[n][(1<<n)-1]); 54 } 55 56 int main(){ 57 scanf("%d%d%d",&n,&m,&p); 58 rep(i,1,m) scanf("%d%d",&x,&y),mp[x-1][y-1]=mp[y-1][x-1]=1; 59 rep(i,0,n-1) scanf("%d",&w[i]); 60 init(); solve(); 61 return 0; 62 }
FWT:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 using namespace std; 6 7 const int N=22,mod=998244353; 8 int n,m,p,x,y,mp[N][N],w[N],fa[N],a[N],d[N],sm[1<<N],sz[1<<N],lg[1<<N],f[N][1<<N],g[N][1<<N]; 9 10 int pow(int a,int b){ return b==0 ? 1 : (b==1 ? a : 1ll*a*a%mod); } 11 int get(int x){ return (fa[x]==x) ? x : fa[x]=get(fa[x]); } 12 13 int ksm(int a,int b){ 14 int res=1; 15 for (; b; a=1ll*a*a%mod,b>>=1) 16 if (b & 1) res=1ll*res*a%mod; 17 return res; 18 } 19 20 void init(){ 21 rep(i,0,n) lg[1<<i]=i; 22 rep(S,1,(1<<n)-1){ 23 int t=S&-S,tot=0; 24 sm[S]=sm[S^t]+w[lg[t]]; sz[S]=sz[S^t]+1; 25 for (int i=S; i; i=i^(i&-i)) a[++tot]=lg[i&-i]; 26 rep(i,1,tot) fa[a[i]]=a[i],d[a[i]]=0; 27 rep(i,1,tot){ 28 rep(j,1,tot) if (mp[a[i]][a[j]]) d[a[i]]^=1,fa[get(a[i])]=get(a[j]); 29 if (d[a[i]]) { g[sz[S]][S]=1; break; } 30 } 31 int f=get(a[1]); 32 rep(i,2,tot) if (f!=get(a[i])){ g[sz[S]][S]=1; break; } 33 } 34 rep(i,0,(1<<n)-1) sm[i]=ksm(sm[i],p),g[sz[i]][i]=1ll*g[sz[i]][i]*sm[i]%mod; 35 } 36 37 void FWT(int a[],int n,int f){ 38 for (int i=2; i<=n; i<<=1) 39 for (int j=0; j<n; j+=i) 40 rep(k,j,j+(i>>1)-1) 41 a[k+(i>>1)]=(1ll*a[k+(i>>1)]+f*a[k]+mod)%mod; 42 } 43 44 void solve(){ 45 f[0][0]=1; FWT(f[0],1<<n,1); 46 rep(i,0,n) FWT(g[i],1<<n,1); 47 rep(i,1,n){ 48 rep(j,0,(1<<n)-1) rep(x,0,i-1) 49 f[i][j]=(f[i][j]+1ll*f[x][j]*g[i-x][j])%mod; 50 FWT(f[i],1<<n,-1); 51 rep(j,0,(1<<n)-1) 52 if (sz[j]==i) f[i][j]=1ll*f[i][j]*ksm(sm[j],mod-2)%mod; else f[i][j]=0; 53 FWT(f[i],1<<n,1); 54 } 55 FWT(f[n],1<<n,-1); printf("%d\n",f[n][(1<<n)-1]); 56 } 57 58 int main(){ 59 scanf("%d%d%d",&n,&m,&p); 60 rep(i,1,m) scanf("%d%d",&x,&y),mp[x-1][y-1]=mp[y-1][x-1]=1; 61 rep(i,0,n-1) scanf("%d",&w[i]); 62 init(); solve(); 63 return 0; 64 }