LOJ #2340. 「WC2018」州区划分(FMT子集卷积)

157 篇文章 0 订阅
92 篇文章 0 订阅

题目
f s f_s fs为集合 s s s ( ∑ w ) p ∗ [ s 合 法 ] (\sum w)^p * [s合法] (w)p[s]
那么可以得到
d p S = 1 f S ∑ T ⊂ S d p T f S − T dp_S = \frac 1{f_S}\sum_{T\subset S} dp_T f_{S-T} dpS=fS1TSdpTfST
因为子集和卷积是按照1的个数逐层转移的,所以这个转移可以和子集和卷积一起转移,
使用FMT复杂度 O ( n 2 2 n ) O(n^2 2^n) O(n22n)

AC Code:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>
#define maxn 21
#define mod 998244353
using namespace std;

int n,m,p;
int c[maxn][maxn],lg[1<<maxn],in[maxn],w[maxn],sum[1<<maxn],isum[1<<maxn],siz[1<<maxn];
int f[maxn+1][1<<maxn],dp[maxn+1][1<<maxn],bit[1<<maxn];

void FMT(int *A,int tp){
	for(int i=0;i<n;i++)
		for(int j=0;j<(1<<n);j++)
			if(j>>i&1){
				if(tp == 1)
					A[j] = (A[j] + A[j^(1<<i)]) % mod;
				else 
					A[j] = (A[j] - A[j^(1<<i)]) % mod;}
}

int vis[maxn],tim;
void dfs(int now,int sta){
	vis[now] = tim;
	for(int i=sta;i;i-=i&-i)
	{
		int u = lg[i&-i];
		if(c[now][u] && vis[u] != tim)
			dfs(u,sta-(i&-i));
	}
}

int Pow(int base,int k){
	int ret = 1;
	for(;k;k>>=1,base=1ll*base*base%mod)
		if(k&1)
			ret=1ll*ret*base%mod;
	return ret;
}

int main(){
	scanf("%d%d%d",&n,&m,&p);
	for(int i=2;i<(1<<n);i++) lg[i] = lg[i>>1] + 1;
	for(int i=1;i<=m;i++){
		int u,v;scanf("%d%d",&u,&v);
		u--,v--;
		c[u][v]=c[v][u]=1;
	}
	for(int i=0;i<n;i++) scanf("%d",&w[i]);
	for(int sta=1;sta<(1<<n);sta++){
		siz[sta] = (siz[sta-(sta&-sta)] + w[lg[sta&-sta]]) % mod;
		memset(in,0,sizeof in);
		bool flg = 0;
		tim++;
		dfs(lg[sta&-sta],sta-(sta&-sta));
		for(int x=sta;x;x-=x&(-x)){
			int u = lg[x&-x] , v;
			if(vis[u]!=tim)
				flg = 1;
			for(int y=sta;y;y-=y&(-y)){
				v = lg[y&-y];
				in[u] ^= c[u][v];
			}
		}
		
		for(int x=sta;x;x-=x&-x)
			if(in[lg[(x&-x)]])
			{	flg = 1;break;}
		if(p == 0) sum[sta] = 1;
		if(p == 1) sum[sta] = siz[sta];
		if(p == 2) sum[sta] = 1ll * siz[sta] * siz[sta] % mod;
		isum[sta] = Pow(sum[sta] , mod-2);
		bit[sta] = bit[sta-(sta&-sta)]+1;
		if(flg)
			f[bit[sta]][sta] = sum[sta];
 	}
	
	dp[0][0] = 1;FMT(dp[0],1);
	for(int i=1;i<=n;i++)
		FMT(f[i],1);
	for(int i=0;i<n;i++){
		FMT(dp[i],-1);
		for(int j=0;j<(1<<n);j++)
		{
			if(bit[j]!=i) dp[i][j] = 0;
			else if(j) dp[i][j] = 1ll * dp[i][j] * isum[j] % mod;
		}
		FMT(dp[i],1);
		for(int k=1;i+k<=n;k++){
			for(int j=0;j<(1<<n);j++)
				if(f[k][j] && dp[i][j]){
					dp[i+k][j] = (dp[i+k][j] + 1ll * dp[i][j] * f[k][j]) % mod;
				}
		}
	}
	if(n) FMT(dp[n],-1);
	printf("%lld\n",(dp[n][(1<<n)-1] * 1ll * isum[(1<<n)-1] % mod +mod)%mod);
}

省选前最后一篇博客了,rp++。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值