bzoj5153&uoj348 【WC2018】州区划分
五十分就是裸的O(3^n)子集dp。
$$f[S]*{w[S]^{p}}=\sum_{T \in S}{f[T]*{w[S-T]^{p}}}$$
然后我们考虑优化这个dp,我们发现这是子集卷积的形式,于是我们就可以用fwt来优化这个dp。
具体的,f[i][S]表示的是S的f值,当且仅当S中1的个数为i,别的f[i][S1]不正确也没有问题,因为我们转移时枚举的是1的个数,所以只有正确的转移会作出正确的贡献。然后就是一个裸的或or异或的fwt。
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <cmath> 6 #define N 2222222 7 #define mod 998244353 8 using namespace std; 9 int can[N],g[N],inv[N],cnt[N],f[25][N],h[25][N]; 10 int n,m,p,to[25],w[25]; 11 int e=1,head[25],fa[25]; 12 struct edge{ 13 int v,next; 14 }ed[2333]; 15 void add(int u,int v){ 16 ed[e].v=v;ed[e].next=head[u];head[u]=e++; 17 } 18 int find(int x){ 19 return x==fa[x]?x:fa[x]=find(fa[x]); 20 } 21 int qp(int a,int b){ 22 int c=1; 23 while(b){ 24 if(b&1)c=1ll*c*a%mod; 25 a=1ll*a*a%mod; b>>=1; 26 } 27 return c; 28 } 29 void UPD(int &a,int b){ 30 a=(a+b>=mod)?(a+b-mod):(a+b); 31 } 32 void IPD(int &a,int b){ 33 a=(a-b<0)?(a-b+mod):(a-b); 34 } 35 bool check(int s){ 36 if(cnt[s]==1)return 0; 37 int a[25],num=0; 38 for(int i=1;i<=n;i++)if(s&(1<<i-1)){ 39 a[++num]=i; 40 int now=s&to[i]; 41 if(!now)return 1; 42 if(cnt[now]&1)return 1; 43 } 44 for(int i=1;i<=num;i++)fa[a[i]]=a[i]; 45 for(int i=1;i<=num;i++){ 46 for(int j=head[a[i]];j;j=ed[j].next)if(s&(1<<ed[j].v-1)) 47 if(find(a[i])!=find(ed[j].v)) 48 fa[find(a[i])]=find(ed[j].v); 49 } 50 for(int i=2;i<=num;i++) 51 if(find(a[i])!=find(a[1]))return 1; 52 return 0; 53 } 54 int calc(int s){ 55 if(!p)return 1; 56 int ans=0; 57 for(int i=1;i<=n;i++)if(s&(1<<i-1)) 58 UPD(ans,w[i]); 59 if(p==1)return ans; 60 return 1ll*ans*ans%mod; 61 } 62 void fwt(int *a){ 63 for(int k=2;k<=(1<<n);k<<=1) 64 for(int i=0;i<(1<<n);i+=k) 65 for(int j=0;j<k>>1;j++) 66 UPD(a[i+j+(k>>1)],a[i+j]); 67 } 68 69 void ifwt(int *a){ 70 for(int k=2;k<=(1<<n);k<<=1) 71 for(int i=0;i<(1<<n);i+=k) 72 for(int j=0;j<k>>1;j++) 73 IPD(a[i+j+(k>>1)],a[i+j]); 74 } 75 int main(){ 76 scanf("%d%d%d",&n,&m,&p); 77 for(int i=0;i<(1<<n);i++)cnt[i]=cnt[i>>1]+(i&1); 78 for(int i=1,u,v;i<=m;i++){ 79 scanf("%d%d",&u,&v); 80 to[u]|=(1<<v-1); 81 to[v]|=(1<<u-1); 82 add(u,v);add(v,u); 83 } 84 for(int i=1;i<=n;i++)scanf("%d",&w[i]); 85 for(int i=1;i<(1<<n);i++){ 86 can[i]=check(i); 87 g[i]=calc(i); 88 inv[i]=qp(g[i],mod-2); 89 } 90 for(int i=1;i<(1<<n);i++) 91 h[cnt[i]][i]=can[i]*g[i]; 92 for(int i=1;i<=n;i++)fwt(h[i]); 93 f[0][0]=1;fwt(f[0]); 94 for(int i=1;i<=n;i++){ 95 for(int j=0;j<i;j++) 96 for(int k=0;k<(1<<n);k++) 97 UPD(f[i][k],1ll*f[j][k]*h[i-j][k]%mod); 98 ifwt(f[i]); 99 for(int j=0;j<(1<<n);j++) 100 f[i][j]=1ll*f[i][j]*inv[j]%mod; 101 if(i^n)fwt(f[i]); 102 } 103 printf("%d\n",f[n][(1<<n)-1]); 104 return 0; 105 }
人生如梦亦如幻 朝如晨露暮如霞。