题目要求的: ∑[ ∑ [ 合法 ](∏ki=1∑x∈Viwx∑ij=1∑x∈Vjwx)pmod998244353 ] ( ∏ i = 1 k ∑ x ∈ V i w x ∑ j = 1 i ∑ x ∈ V j w x ) p mod 998244353
合法条件: ∑|Vi|=n,|⋃Vi|=n,Vi ∑ | V i | = n , | ⋃ V i | = n , V i 没有欧拉回路
考虑一个 DP,fs=∑t∈sfs−tgt(valtvals)p D P , f s = ∑ t ∈ s f s − t g t ( v a l t v a l s ) p
fs f s 是当前划分集合是 s s 的答案, gs g s 表示集合 s s 是否合法, vals v a l s 就是 s s 的权值和
可以看出这个 DP D P 是 3n 3 n (枚举子集)的
发现这个玩意 ∑t∈sfs−tgt ∑ t ∈ s f s − t g t 是一个子集卷积(不会的转 2015vfk 2015 v f k 集训队论文)
fs=∑a⋃b=s,a⋂b=∅fagb(valbvals)p f s = ∑ a ⋃ b = s , a ⋂ b = ∅ f a g b ( v a l b v a l s ) p
并集可以直接 FMT F M T 然后点乘得到,交集就不好玩了
考虑 a⋂b=∅⇒|a|+|b|=|s| a ⋂ b = ∅ ⇒ | a | + | b | = | s |
这样就可以把交集为空的限制去掉了
fi,s f i , s 表示 s s 中 1 1 的个数是 i i 的答案, gi,s=[s g i , s = [ s 合法 ]∗vals ] ∗ v a l s
⇒fi,sgi,s=∑i−1j=0∑t∈sfj,s−tgi−j,t ⇒ f i , s g i , s = ∑ j = 0 i − 1 ∑ t ∈ s f j , s − t g i − j , t
直接把 fj,gi−jFMT f j , g i − j F M T 一下再点乘一下再 IFMT I F M T 一下得到 fi,sgi,s f i , s g i , s
再乘以 gi,s g i , s 的逆元就可以得到 fi,s f i , s 了
最后的答案就是 fn,2n−1 f n , 2 n − 1
其他的看代码把(参考了一下 immortalCO i m m o r t a l C O 大佬的代码)
代码里那个怎么判断是不是欧拉回路主要是两点
①:一个点到当前集合的点的数量的奇数个
②:从一点出发,用状压模拟 dfs d f s ,看看能不能走完这个点集
感觉好妙啊, %%%immortalCO % % % i m m o r t a l C O
#include
#define fp(i,a,b) for(register int i=a,I=b+1;i
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define go(i,u) for(register int i=fi[u],v=e[i].to;i;v=e[i=e[i].nx].to)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
char ss[1<<17],*A=ss,*B=ss;
inline char gc(){return A==B&&(B=(A=ss)+fread(ss,1,1<<17,stdin),A==B)?-1:*A++;}
template<class T>inline void sd(T&x){
char c;T y=1;while(c=gc(),(c<48||571)if(c==45)y=-1;x=c-48;
while(c=gc(),4758)x=x*10+c-48;x*=y;
}
char sr[1<<21],z[20];int C=-1,Z;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
template<class T>inline void we(T x){
if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
while(z[++Z]=x%10+48,x/=10);
while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=22,M=1<<21,P=998244353;
typedef int arr1[N];
typedef int arr2[M];
int n,m,k,S;arr1 e,Mi;arr2 w,g[N],f[N],nx,cnt,pos,iw;long long tp[M];
inline int inv(int x){return x^1?P-1ll*(P/x)*inv(P%x)%P:1;}
inline int fpm(int a){int x=1;for(int b=k;b;b>>=1,a=1ll*a*a%P)if(b&1)x=1ll*x*a%P;return x;}
inline int add(int a,int b){return a+=b,a>=P?a-P:a;}
inline int sub(int a,int b){return a-=b,a<0?a+P:a;}
inline void fmt(int*a,int op(int,int)){
fp(i,1,n)fp(s,1,S)if(s&Mi[i])
a[s]=op(a[s],a[s^Mi[i]]);
}
int main(){
#ifndef ONLINE_JUDGE
file("walk");
#endif
sd(n),sd(m),sd(k);int u,v;
pos[Mi[1]=1]=1;fp(i,2,n)pos[Mi[i]=Mi[i-1]<<1]=i;
while(m--)
sd(u),sd(v),
e[u]|=Mi[v],e[v]|=Mi[u];
fp(i,1,n)sd(w[Mi[i]]);S=(1<1;
fp(i,1,S)nx[i]=i&-i,cnt[i]=cnt[i^nx[i]]+1;
fp(s,1,S){
w[s]=w[nx[s]]+w[s^nx[s]],iw[s]=inv(fpm(w[s]));
int t=nx[s];
for(int i=s;i;i-=nx[i])
if(cnt[e[pos[nx[i]]]&s]&1)goto NotEuler;
for(int i=nx[s],p;i;)
p=pos[nx[i]],i-=nx[i],
i|=e[p]&s&~t,t|=e[p]&s;
if(s==t)continue;
NotEuler:g[cnt[s]][s]=fpm(w[s]);
}f[0][0]=1;fmt(f[0],add);int*a,*b;
fp(i,1,n){
fmt(g[i],add);memset(tp,0,8*S+8);
fp(j,0,i-1){
a=f[j],b=g[i-j];
fp(s,0,S)tp[s]+=1ll*a[s]*b[s];
}a=f[i];
fp(s,0,S)a[s]=tp[s]%P;
fmt(a,sub);
fp(s,0,S)a[s]=1ll*a[s]*iw[s]%P;
if(iprintf("%d",f[n][S]);
return Ot(),0;
}