uoj#348/洛谷P4221 [WC2018]州区划分(FWT)
傳送門(uoj)
傳送門(洛谷)
全世界都會子集卷積就咱不會……全世界都在寫\(FMT\)就咱只會\(FWT\)……
前置芝士
或運算\(FWT\)或者\(FMT\)
左轉(zhuǎn)洛谷模板區(qū),包教包會
子集卷積
定義:對于兩個集合冪級數(shù)\(F,G\),它們的子集卷積\(H\)定義為\[H_S=\sum_{T\subseteq S}F_TG_{S-T}\]
簡單來說就是兩個下標要滿足的條件為\(L\cap R=\varnothing\)且\(L\cup R=S\)
\(L\cup R=S\)就是個異或卷積的事兒,關(guān)鍵是\(L\cap R=\varnothing\)太麻煩了
轉(zhuǎn)化一下,\(L\cap R=\varnothing\)等價于\(|L|+|R|=|S|\)
那么我們可以新加一維,設\(f_{i,S}\)表示集合大小為\(i\),集合為\(S\),那么只有在\(i=|S|\)的時候這才是個對的東西。一開始的時候,我們可以把所有的\(f_{|S|,S}\)賦值為原來的\(f(S)\)(\(g\)同理),然后不斷枚舉\(i\),做完\(FWT\)之后令\(h_{i,S}=\sum_{j=0}^if_{j,S}g_{i-j,S}\)。每個\(i\)做完后,把那些\(|S|\neq i\)的\(h_{i,S}\)清零就好了
如果不是很明白為什么的話,可以這樣理解。首先對于所有的\(j<i\),\(f_j,g_j\)里面存的都是正確的值。根據(jù)\(FWT\)的性質(zhì),未清零之前\(h_{i,S}\)中每一個\(S\)都是由那些\(L\cup R=S\)的\(f_L\)和\(g_R\)得來的,只要除去那些\(|L|+|R|\neq |S|\)的,剩下的肯定正確
本題題解
設\(f_S\)為選點集合為\(S\)時的貢獻之和,\(g_S\)當\(S\)是一個合法集合時為\({sum_S}^p\),當\(S\)不合法時為\(0\)
那么遞推式就是\[f_S=\frac{1}{{sum_S}^p}\sum_{T\subset S}f_Tg_{S-T}\]
似乎有點眼熟……話說這個不就是個子集卷積么……
等會兒?這玩意兒是自己卷自己啊?
但我們發(fā)現(xiàn)這里每一個數(shù)只會被自己的子集卷到,于是我們依然可以枚舉\(i\),那么所有\(|T|<|S|\)的\(f_{|T|,T}\)已經(jīng)算好了,直接帶進去卷就好了
另外,\(S\)是個合法集合就是說\(S\)不存在歐拉回路,那么\(S\)存在歐拉回路的充要條件就是\(S\)連通且每個點度數(shù)都是偶數(shù),判一下就好了
所以咱真的不知道為啥大家都寫\(FMT\),咱寫\(FMT\)比\(FWT\)慢好多啊……
//minamoto #include<bits/stdc++.h> #define R register #define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i) #define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i) #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v) using namespace std; char buf[1<<21],*p1=buf,*p2=buf; inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;} int read(){R int res,f=1;R char ch;while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');return res*f; } char sr[1<<21],z[20];int C=-1,Z=0; inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;} void print(R int x){if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;while(z[++Z]=x%10+48,x/=10);while(sr[++C]=z[Z],--Z);sr[++C]='\n'; } const int N=(1<<21)+5,P=998244353; inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;} inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;} inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;} int ksm(R int x,R int y){R int res=1;for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);return res; } int sz[N],sum[N],inv[N],f[25][N],g[25][N],w[25],p[25],fa[25],deg[25],bin[25]; int lim,n,m,q,u,v; int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} void dft(int *A){for(R int mid=1;mid<lim;mid<<=1)for(R int j=0;j<lim;j+=(mid<<1))for(R int k=0;k<mid;++k)A[j+k+mid]=add(A[j+k+mid],A[j+k]); } void idft(int *A){for(R int mid=1;mid<lim;mid<<=1)for(R int j=0;j<lim;j+=(mid<<1))for(R int k=0;k<mid;++k)A[j+k+mid]=dec(A[j+k+mid],A[j+k]); } int calc(R int S){if(!q)return 1;R int res=0;fp(i,1,n)if(S>>(i-1)&1)res=add(res,w[i]);return q&1?res:mul(res,res); } bool ck(R int S){if(sz[S]==1)return 0;int k=sz[S];fp(i,1,n)deg[i]=0,fa[i]=i;fp(i,1,n)if(S&(1<<i-1)){sum[S]+=w[i];fp(j,i+1,n)if((S&(1<<j-1))&&(p[i]&(1<<j-1))){++deg[i],++deg[j];if(find(i)!=find(j))fa[fa[i]]=fa[j],--k;}}sum[S]=q==0?1:q==1?sum[S]:sum[S]*sum[S];if(k>1)return true;fp(i,1,n)if((S&(1<<i-1))&&(deg[i]&1))return true;return false; } int main(){ // freopen("testdata.in","r",stdin);n=read(),m=read(),q=read(),lim=(1<<n);fp(i,1,m)u=read(),v=read(),p[u]|=(1<<v-1),p[v]|=(1<<u-1);fp(i,1,n)w[i]=read();fp(i,1,lim-1)sz[i]=sz[i>>1]+(i&1);fp(i,1,lim-1){g[sz[i]][i]=ck(i)?sum[i]:0;inv[i]=ksm(sum[i],P-2);}fp(i,0,n)dft(g[i]);f[0][0]=1,dft(f[0]);fp(i,1,n){fp(j,0,i-1)fp(k,0,lim-1)f[i][k]=add(f[i][k],mul(f[j][k],g[i-j][k]));idft(f[i]);fp(k,0,lim-1)f[i][k]=sz[k]==i?mul(f[i][k],inv[k]):0;if(i!=n)dft(f[i]);}printf("%d\n",f[n][lim-1]);return 0; }轉(zhuǎn)載于:https://www.cnblogs.com/bztMinamoto/p/10284392.html
總結(jié)
以上是生活随笔為你收集整理的uoj#348/洛谷P4221 [WC2018]州区划分(FWT)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 美团点评CTO罗道锋确认离职,新东家是快
- 下一篇: 洛谷P4239 【模板】多项式求逆(加强