【WC2018】州區劃分(FWT,動態規劃)
阿新 • • 發佈:2018-12-28
cap %d void turn href style int () bre
聽說這個玩意叫做子集卷積。
要做的就是\(\sum f[T]*g[W]*[T\cup W=S,T\cap W=\phi]\)
可以用二進制下\(1\)的個數來表示這個限制\(\sum f[T]*g[W]*[T\cup W=S,cnt(T)+cnt(W)=cnt(S)]\)。
其中\(cnt(S)\)表示\(S\)中\(1\)的個數。
然後聽說這個玩意就是一個套路了。。。
把集合的\(1\)的個數強制作為一維狀態加上去。也就是\(f[cnt(S)][S]\)這樣子。
令\(g[cnt(S)][S]=(W_S)^p*check[S]\)。
這樣子的話只需要枚舉兩者的\(1\)的個數就可以去掉位的限制,然後就只剩下兩者的交是\(S\) 的限制,這個限制可以直接表示為或卷積(異或似乎也行???)。
那麽直接\(FWT\)按層處理即可。
【WC2018】州區劃分(FWT,動態規劃)
題面
UOJ
洛谷
題解
首先有一個暴力做法(就有\(50\)分了)
先\(O(2^nn^2)\)預處理出每個子集是否合法,然後設\(f[S]\)表示當前的答案,每次枚舉一個子集進行轉移,得到方程:\(\displaystyle f[S]=(\frac{1}{W_s})^p\sum_{T\subset S}f[T]*(W_{S-T})^p*check[S-T]\)。
其中\(W\)表示權值和,\(check\)表示是否合法。
這樣子的復雜度是\(O(3^n)\),然後似乎可以拿\(50\)分了。
後面那個東西很像一個卷積,然而直接\(or\)卷積的話,會算出很多我們不想要的東西。
要做的就是\(\sum f[T]*g[W]*[T\cup W=S,T\cap W=\phi]\)
可以用二進制下\(1\)的個數來表示這個限制\(\sum f[T]*g[W]*[T\cup W=S,cnt(T)+cnt(W)=cnt(S)]\)。
其中\(cnt(S)\)表示\(S\)中\(1\)的個數。
然後聽說這個玩意就是一個套路了。。。
把集合的\(1\)的個數強制作為一維狀態加上去。也就是\(f[cnt(S)][S]\)這樣子。
令\(g[cnt(S)][S]=(W_S)^p*check[S]\)。
這樣子的話只需要枚舉兩者的\(1\)的個數就可以去掉位的限制,然後就只剩下兩者的交是\(S\)
那麽直接\(FWT\)按層處理即可。
#include<iostream> #include<cstdio> using namespace std; #define MOD 998244353 #define MAX 25 void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;} inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int fpow(int a,int b) { int s=1; while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;} return s; } int n,m,p,S,G[MAX],dg[MAX],lg[1<<21]; bool chk[1<<21]; int cc[1<<21]; int f[22][1<<21],g[22][1<<21],tmp[1<<21]; int w[MAX],W[1<<21],Wk[1<<21],invW[1<<21]; void FWT(int *a,int opt) { for(int i=1;i<S;i<<=1) for(int p=i<<1,j=0;j<S;j+=p) for(int k=0;k<i;++k) if(opt==1)add(a[i+j+k],a[j+k]); else add(a[i+j+k],MOD-a[j+k]); } int fa[MAX]; int getf(int x){return x==fa[x]?x:fa[x]=getf(fa[x]);} int main() { n=read();m=read();p=read();S=1<<n; for(int i=2;i<S;++i)lg[i]=lg[i>>1]+1; for(int i=1;i<=m;++i) { int u=read()-1,v=read()-1; G[u]|=1<<v;G[v]|=1<<u; } for(int i=0;i<n;++i)w[i]=read(); for(int i=1;i<S;++i)cc[i]=cc[i>>1]+(i&1); for(int i=0;i<S;++i) { for(int j=0;j<n;++j)fa[j]=j; for(int j=0;j<n;++j)if(i&(1<<j))dg[j]=cc[G[j]&i]; for(int j=0;j<n;++j) if(i&(1<<j)) { int x=i&G[j]; while(x)fa[getf(j)]=getf(lg[x&(-x)]),x-=x&(-x); } chk[i]=false;int nw=0; for(int j=0;j<n;++j)if(i&(1<<j))W[i]+=w[j],nw=j; for(int j=0;j<n&&!chk[i];++j)if((dg[j]&1)||((i&(1<<j))&&dg[j]==0))chk[i]=true; for(int j=0;j<n;++j)dg[j]=0; for(int j=0;j<n&&!chk[i];++j)if(i&(1<<j))if(getf(j)!=getf(nw))chk[i]=true; if(cc[i]==1)chk[i]=false; Wk[i]=fpow(W[i],p);invW[i]=fpow(Wk[i],MOD-2); if(chk[i])g[cc[i]][i]=Wk[i]; } f[0][0]=1;FWT(f[0],1); for(int i=1;i<=n;++i) { FWT(g[i],1); for(int j=0;j<i;++j) for(int k=0;k<S;++k) add(tmp[k],1ll*f[j][k]*g[i-j][k]%MOD); FWT(tmp,-1); for(int j=0;j<S;++j)f[i][j]=1ll*tmp[j]*invW[j]%MOD,tmp[j]=0; if(i!=n)FWT(f[i],1); } /* f[0]=1; for(int i=1;i<S;++i) { for(int t=(i-1)&i;;t=(t-1)&i) { if(chk[i^t])add(f[i],1ll*f[t]*Wk[i^t]%MOD); if(!t)break; } f[i]=1ll*f[i]*invW[i]%MOD; } */ printf("%d\n",f[n][S-1]); return 0; }
【WC2018】州區劃分(FWT,動態規劃)