[LOJ]#2340. 「WC2018」州区划分 FWT+欧拉回路

Solution

首先对于每个子集判断是否可以单独成一个州,也就是判断是否存在欧拉回路,存在欧拉回路当且仅当图连通且每个点的度数都为偶数。设 s u m S sum_S sumS为集合 S S S所有元素 w w w之和,若 S S S能成一个州, g S = s u m S p g_S={sum_S}^p gS=sumSp,设 f S f_S fS为集合 S S S所有划分方案的乘积之和,那么 f S = 1 s u m S p ∑ f T g S − T , T & ( S − T ) = 0 f_S={1\over {sum_S}^p}\sum f_{T}g_{S-T},T\&(S-T)=0 fS=sumSp1fTgST,T&(ST)=0。这就是个子集卷积,不过会自己卷自己,要按照 1 1 1的个数分层转移。
不知道为什么,用异或卷积比用或卷积做慢了 10 s 10s 10s……

Code

#include
using namespace std;
#define LL long long
#define pa pair
const int inf=2147483647;
const int mod=998244353,inv=(mod+1)/2;
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
inline void upd(int&x,int y){x+=y;if(x>=mod)x-=mod;}
int Pow(int x,int y)
{
	if(!y)return 1;
	int t=Pow(x,y>>1),re=(LL)t*t%mod;
	if(y&1)re=(LL)re*x%mod;
	return re;
}
int n,m,p,w[22],f[22][1<<21],g[22][1<<21],s[1<<21],o[1<<21],invs[1<<21],deg[22];
vector<int>h[22];
int tS;
void dfs(int x,int S)
{
	tS|=(1<<(x-1));
	for(int i=0;i<h[x].size();i++)
	{
		int y=h[x][i];
		if(S&(1<<(y-1))&&!(tS&(1<<(y-1))))dfs(y,S);
	}
}
LL sb=0;
void fwt(int *a,int o)
{
	for(int i=0;i<n;i++)
	for(int j=(1<<n)-1;j>=0;j--)
	if(j&(1<<i))
	{
		if(o==1)a[j]=(a[j]+a[j^(1<<i)])%mod;
		else a[j]=(a[j]-a[j^(1<<i)]+mod)%mod;
	}
}
int main()
{
	n=read(),m=read(),p=read();int N=1<<n;
	o[0]=0;for(int i=1;i<(1<<n);i++)o[i]=o[i>>1]+(i&1);
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read();
		h[x].push_back(y),h[y].push_back(x);
	}
	for(int i=1;i<=n;i++)w[i]=read();
	for(int S=1;S<(1<<n);S++)
	{
		s[S]=0;int pp;bool flag=true;
		for(int i=1;i<=n;i++)deg[i]=0;
		for(int i=1;i<=n;i++)
		if((1<<(i-1))&S)
		{
			s[S]+=w[i],pp=i;
			for(int j=0;j<h[i].size();j++)
			{
				int y=h[i][j];if(y<i)continue;
				if((1<<(y-1))&S)deg[y]++,deg[i]++;
			}
		}
		for(int i=1;i<=n;i++)
		if((1<<(i-1))&S&&deg[i]&1)flag=false;
		tS=0;dfs(pp,S);invs[S]=Pow(Pow(s[S],mod-2),p);
		if(tS==S&&flag)g[o[S]][S]=0;else g[o[S]][S]=Pow(s[S],p);
	}
	for(int i=1;i<=n;i++)fwt(g[i],1);
	f[0][0]=1;fwt(f[0],1);
	for(int S=1;S<(1<<n);S<<=1)f[1][S]=(g[1][S]?1:0);fwt(f[1],1);
	for(int i=2;i<=n;i++)
	{
		for(int j=1;j<=i;j++)
		for(int S=0;S<N;S++)
		upd(f[i][S],(LL)f[i-j][S]*g[j][S]%mod);
		fwt(f[i],-1);
		for(int S=0;S<N;S++)
		if(o[S]!=i)f[i][S]=0;
		else f[i][S]=(LL)f[i][S]*invs[S]%mod;
		fwt(f[i],1);
	}
	fwt(f[n],-1);
	printf("%d",f[n][(1<<n)-1]);
}

你可能感兴趣的:(FWT,欧拉回路)