题目
设
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=fS1∑T⊂SdpTfS−T
因为子集和卷积是按照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++。