[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\)大小
再直接FWT
或FMT
转移
这样如果两个集合有交集贡献将是\(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]);
}