[WC2018]州区划分

题意

题面

给一张带点权的无向图

要求对其划分为联通且不存在欧拉回路的多个子图

定义一个子图的贡献是

\(i\)个子图的点权和占前\(i\)个子图的点权和的比例的\(p\)次幂

定义一个划分的贡献是

该划分下所有子图的贡献的乘积

求所有划分的贡献之和

题解

\(f_S\)为选取点集为\(S\)时所有划分的贡献和

\[f_S=\frac{\sum_{T \in S}{f_{S-T}*Sum_T^p}}{Sum_S^p} \]

注意要求\(T\)合法

可以枚举预处理

只需保证联通(并查集)

且没有欧拉回路(所有点度数为偶数)

至此复杂度\(O(3^n)\)

考虑优化

分子部分显然是集合卷积的形式

但与普通的或卷积不同

这个式子要求两个集合交集为空才能转移

这里有一个大智若愚、苦尽甘来、有舍有得、其实就是我太菜了的做法

加一维状态\(i\)表示所选点集的大小

这样复杂度就乘上一个$n^2 $啦

就可以直接或卷积转移

具体说就是

枚举点集\(S\)\(S-T\)大小

再直接FWTFMT转移

这样如果两个集合有交集贡献将是\(0\)

复杂度\(O(n^22^n)\)

#include<bits/stdc++.h>

using namespace std;

#define gc c=getchar()
#define r(x) read(x)
#define ll long long 

template<typename T>
inline void read(T&x){
    x=0;T k=1;char gc;
    while(!isdigit(c)){if(c=='-')k=-1;gc;}
    while(isdigit(c)){x=x*10+c-'0';gc;}x*=k;
}

const int N=25;
const int S=1<<21|7;
const int p=998244353;

struct Edge{
	int u,v;
}E[N*N];

int fa[N];

int find(int x){
	return x==fa[x]?x:fa[x]=find(fa[x]);
}

inline void uni(int u,int v){
	u=find(u),v=find(v);
	if(u!=v)fa[u]=v;
}

inline bool in(int S,int x){
	return S>>(x-1)&1;
}

int n,m,q;

int deg[N];

inline bool check(int S){
	for(int i=1;i<=n;++i){
		fa[i]=i;
		deg[i]=0;
	}
	for(int i=1;i<=m;++i){
		int u=E[i].u,v=E[i].v;
		if(in(S,u)&&in(S,v)){
			uni(u,v);
			++deg[u];
			++deg[v];
		}
	}
	int lst=0;
	for(int i=S;i;i^=i&(-i)){
		int x=__builtin_ffs(i);
		if(deg[x]&1)return 1;
		if(lst&&find(x)!=lst)return 1;
		lst=find(x);
	}
	return 0;
}

inline void add(int &a,int b){
	a+=b;
	if(a>=p)a-=p;
}

inline void sub(int &a,int b){
	a-=b;
	if(a<0)a+=p;
}

inline ll qpow(ll a,ll b){
	ll ans=1;
	while(b){
		if(b&1)(ans*=a)%=p;
		(a*=a)%=p;
		b>>=1;
	}
	return ans;
}

inline void fmt(int *A){
	for(int i=0;i<n;++i){
		for(int j=0;j<1<<n;++j){
			if(j>>i&1)add(A[j],A[j^(1<<i)]);
		}
	}
}

inline void ifmt(int *A){
	for(int i=0;i<n;++i){
		for(int j=0;j<1<<n;++j){
			if(j>>i&1)sub(A[j],A[j^(1<<i)]);
		}
	}
}

int w[N];
int sum[S];
int Inv[S];
int bit[S];
int g[N][S];
int f[N][S];

int main(){
	r(n),r(m),r(q);
	for(int i=1;i<=m;++i){
		int u,v;r(u),r(v);
		E[i]=Edge{u,v};
	}
	for(int i=1;i<=n;++i){
		r(w[i]);
	}
	int S=(1<<n)-1;
	for(int i=1;i<=S;++i){
		sum[i]=w[__builtin_ffs(i)]+sum[i^(i&(-i))];
		bit[i]=bit[i>>1]+(i&1);
		int tmp=qpow(sum[i],q);
		g[bit[i]][i]=check(i)*tmp;
		Inv[i]=qpow(tmp,p-2);
	}
	for(int i=1;i<=n;++i)fmt(g[i]);
	f[0][0]=1;fmt(f[0]);
	for(int i=1;i<=n;++i){
		for(int j=0;j<i;++j){
			for(int k=0;k<1<<n;++k){
				add(f[i][k],(ll)f[j][k]*g[i-j][k]%p);
			}
		}
		ifmt(f[i]);
		for(int k=0;k<1<<n;++k)f[i][k]=i==bit[k]?(ll)f[i][k]*Inv[k]%p:0;
		if(i^n)fmt(f[i]);
	}
	printf("%d\n",f[n][S]);
}

posted @ 2018-12-27 15:28  NamelessOIer  阅读(200)  评论(0编辑  收藏  举报