• 【WC2018】州区划分(FWT,动态规划)


    【WC2018】州区划分(FWT,动态规划)

    题面

    UOJ
    洛谷

    题解

    首先有一个暴力做法(就有(50)分了)
    (O(2^nn^2))预处理出每个子集是否合法,然后设(f[S])表示当前的答案,每次枚举一个子集进行转移,得到方程:(displaystyle f[S]=(frac{1}{W_s})^psum_{Tsubset S}f[T]*(W_{S-T})^p*check[S-T])
    其中(W)表示权值和,(check)表示是否合法。
    这样子的复杂度是(O(3^n)),然后似乎可以拿(50)分了。
    后面那个东西很像一个卷积,然而直接(or)卷积的话,会算出很多我们不想要的东西。
    听说这个玩意叫做子集卷积。
    要做的就是(sum f[T]*g[W]*[Tcup W=S,Tcap W=phi])
    可以用二进制下(1)的个数来表示这个限制(sum f[T]*g[W]*[Tcup 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
    ",f[n][S-1]);
    	return 0;
    }
    
  • 相关阅读:
    数据清洗
    JAVA多线程三种实现方式
    QT-4.8.6 编译配置过程
    qt 编译问题总结
    [转载]tslib1.4与Qt4.8.6的交叉编译与移植
    STC12C5A60S2 @ 22.0184Mhz 精确延时
    STC12C5A60S2 双串口通信
    C# Bitmap 复制
    TextMate2 最新版下载及源码编译过程
    mac系统 PHP Nginx环境变量修改
  • 原文地址:https://www.cnblogs.com/cjyyb/p/10190522.html
Copyright © 2020-2023  润新知