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 }
View Code

 

posted @ 2018-04-18 21:44  Ren_Ivan  阅读(266)  评论(0编辑  收藏  举报