zoukankan      html  css  js  c++  java
  • uoj#348/洛谷P4221 [WC2018]州区划分(FWT)

    传送门(uoj)

    传送门(洛谷)

    全世界都会子集卷积就咱不会……全世界都在写(FMT)就咱只会(FWT)……

    前置芝士

    或运算(FWT)或者(FMT)

    左转洛谷模板区,包教包会

    子集卷积

    定义:对于两个集合幂级数(F,G),它们的子集卷积(H)定义为$$H_S=sum_{Tsubseteq S}F_TG_{S-T}$$
    简单来说就是两个下标要满足的条件为(Lcap R=varnothing)(Lcup R=S)

    (Lcup R=S)就是个异或卷积的事儿,关键是(Lcap R=varnothing)太麻烦了

    转化一下,(Lcap 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| eq i)(h_{i,S})清零就好了

    如果不是很明白为什么的话,可以这样理解。首先对于所有的(j<i)(f_j,g_j)里面存的都是正确的值。根据(FWT)的性质,未清零之前(h_{i,S})中每一个(S)都是由那些(Lcup R=S)(f_L)(g_R)得来的,只要除去那些(|L|+|R| eq |S|)的,剩下的肯定正确

    本题题解

    (f_S)为选点集合为(S)时的贡献之和,(g_S)(S)是一个合法集合时为({sum_S}^p),当(S)不合法时为(0)

    那么递推式就是$$f_S=frac{1}{{sum_S}^p}sum_{Tsubset S}f_Tg_{S-T}$$

    似乎有点眼熟……话说这个不就是个子集卷积么……

    等会儿?这玩意儿是自己卷自己啊?

    但我们发现这里每一个数只会被自己的子集卷到,于是我们依然可以枚举(i),那么所有(|T|<|S|)(f_{|T|,T})已经算好了,直接带进去卷就好了

    另外,(S)是个合法集合就是说(S)不存在欧拉回路,那么(S)存在欧拉回路的充要条件就是(S)连通且每个点度数都是偶数,判一下就好了

    所以咱真的不知道为啥大家都写(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]='
    ';
    }
    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
    ",f[n][lim-1]);
    	return 0;
    }
    
  • 相关阅读:
    iOS-iOS8模拟器设置中文键盘
    iOS-应用性能调优的25个建议和技巧
    IOS-通讯录
    IOS-录音
    IOS-视频
    IOS-音乐
    IOS-音效
    iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式
    Android 带你玩转实现游戏2048 其实2048只是个普通的控件(转)
    Android TagFlowLayout完全解析 一款针对Tag的布局(转)
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/10284392.html
Copyright © 2011-2022 走看看