传送门
首先我们可以列个dp方程出来:
d
p
S
=
(
1
w
S
)
p
∑
T
⊆
S
d
p
T
∗
(
w
S
−
T
)
p
dp_S=({\frac 1 {w_{S}}})^p \sum_{T\subseteq S}dp_T*(w_{S-T})^p
dpS=(wS1)p∑T⊆SdpT∗(wS−T)p
然后这样枚举子集暴力dp是
O
(
3
n
)
O(3^n)
O(3n)的
然后我们可以加一维变成子集卷积
d
p
i
,
S
dp_{i,S}
dpi,S
然后直接子集卷积做就好了
O
(
n
2
2
n
)
O(n^22^n)
O(n22n)
这题不知道是卡常数还是我写的丑,反正就是最慢的点14s
顺带一提,这个题我还试了试刚刚学的FMT,看代码常数挺小,可是我还是这么慢qwq
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#define LL long long
using namespace std;
inline int read(){
int x=0,f=1;char ch=' ';
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;
}
const int N=1<<21,mod=998244353;
int n,m,p,tot,L;
int head[N],to[N],Next[N];
int deg[N],fa[N],v[N],can[N];
int w[N],inv[N],dp[22][N],g[22][N];
inline void addedge(int x,int y){to[++tot]=y;Next[tot]=head[x];head[x]=tot;}
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline LL ksm(LL a,LL n){
LL ans=1LL;
while(n){
if(n&1)ans=ans*a%mod;
a=a*a%mod;
n>>=1;
}
return ans;
}
inline bool check(int S){
int cnt=0;
for(int i=0;i<n;++i)if((S>>i)&1)fa[i]=i,++cnt,deg[i]=0,w[S]+=v[i];
for(int i=0;i<n;++i){
if(!((S>>i)&1))continue;
for(int j=head[i];j;j=Next[j]){
int u=to[j];
if(!((S>>u)&1))continue;
++deg[i];++deg[u];
int fi=find(i),fu=find(u);
if(fi!=fu)fa[fi]=fu,--cnt;
}
}
if(cnt>1)return true;
cnt=0;
for(int i=0;i<n;++i)if(((S>>i)&1) && (deg[i]&1))++cnt;
if(cnt==0)return false;
return true;
}
inline LL calc(LL x){
if(!p)return 1;
else if(p==1)return x%mod;
else return x*x%mod;
}
inline void FMT(int *a){
for(int i=1;i<L;i<<=1)
for(int j=0;j<L;++j)
if(i&j)a[j]=(a[j]+a[j^i])%mod;
}
inline void IFMT(int *a){
for(int i=1;i<L;i<<=1)
for(int j=0;j<L;++j)
if(i&j)a[j]=(a[j]-a[j^i]+mod)%mod;
}
int main(){
n=read();m=read();p=read();L=1<<n;
for(int i=1;i<=m;++i){
int x=read()-1,y=read()-1;
addedge(x,y);
}
for(int i=0;i<n;++i)v[i]=read();
for(int i=0;i<L;++i)if(check(i))can[i]=1;
for(int i=0;i<L;++i)w[i]=calc(w[i]);
for(int k=0;k<L;++k){
int cnt=0;
for(int i=0;i<n;++i)if((k>>i)&1)++cnt;
if(can[k])g[cnt][k]=w[k];
inv[k]=ksm(w[k],mod-2);
}
dp[0][0]=1;FMT(dp[0]);
for(int i=0;i<=n;++i)FMT(g[i]);
for(int i=1;i<=n;++i){
for(int j=0;j<i;++j)
for(int k=0;k<L;++k)
dp[i][k]=(dp[i][k]+1LL*dp[j][k]*g[i-j][k])%mod;
IFMT(dp[i]);
for(int k=1;k<L;++k)dp[i][k]=1LL*dp[i][k]*inv[k]%mod;
if(i==n)break;
FMT(dp[i]);
}
printf("%lld",dp[n][L-1]);
return 0;
}