UOJ
做的时候SB了,纠结了好久怎么判定欧拉回路,YY了半天状压DP无果,后来突然想起欧拉回路的充要条件是联通且点的度数为偶数。
设 h [ s ] = ∑ x ∈ s w x h[s]=\sum_{x\in s} w_x h[s]=∑x∈swx,如果 s s s是合法的那么 g [ s ] = h [ s ] g[s]=h[s] g[s]=h[s],否则 g [ s ] = 0 g[s]=0 g[s]=0
那么枚举最后的划分出来的子集
f [ s ] = 1 h [ s ] k ∑ t ⊆ s g [ t ] k × f [ s − t ] f[s]=\frac 1 {h[s]^k}\sum_{t\subseteq s} g[t]^k \times f[s-t] f[s]=h[s]k1t⊆s∑g[t]k×f[s−t]
这个就是一个子集卷积。子集卷积有一个 O ( n 2 2 n ) O(n^22^n) O(n22n)的方法,就是按元素个数从小到大计算 f [ S ] f[S] f[S]。在计算 f [ S ] f[S] f[S]的时候,使有 j j j个元素的 g g g和有 i − j i-j i−j个元素的 f f f做一次或卷积,为了限定两个集合无交集,卷积后如果元素个数小于 i i i个,我们就去掉它的贡献。
时间复杂度 O ( n 2 2 n ) O(n^22^n) O(n22n),空间复杂度 O ( n 2 n ) O(n2^n) O(n2n)。
#include
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long ll;
const int maxn=2100000,mod=998244353;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
x=0;int f=0;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
if(f) x=-x;
}
int n,m,p,N,w[25],e[25][25],fa[25],cnt[maxn],lg[maxn];
int f[22][maxn],g[22][maxn],ig[maxn];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int merge(int x,int y)
{
int fx=find(x),fy=find(y);
if(fx==fy) return 0;
return fa[fy]=fx;
}
int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
int power(int x,int y)
{
int res=1;
for(;y;y>>=1,x=(ll)x*x%mod)
if(y&1)
res=(ll)res*x%mod;
return res;
}
int check(int s)
{
static int top,tot,stk[25],d[25];
if(cnt[s]==1) return 0;
top=0;
for(;s;s-=lowbit(s)) stk[++top]=lg[lowbit(s)],d[top]=0,fa[top]=top;
tot=top;
for(int i=1;i<=top;i++)
for(int j=1;j<i;j++)
if(e[stk[i]][stk[j]])
{
++d[i],++d[j];
if(merge(i,j)) tot--;
}
if(tot>1) return 1;
for(int i=1;i<=top;i++) if(d[i]&1) return 1;
return 0;
}
void input()
{
int u,v;
read(n);read(m);read(p);N=1<<n;
for(int i=1;i<=m;i++){read(u);read(v);e[u][v]=e[v][u]=1;}
for(int i=1;i<=n;i++) read(w[i]),lg[1<<i-1]=i,g[1][1<<i-1]=w[i];
for(int i=1;i<N;i++)
{
cnt[i]=cnt[i>>1]+(i&1);
if(cnt[i]>1) g[cnt[i]][i]=g[cnt[i]-1][i-lowbit(i)]+w[lg[lowbit(i)]];
}
for(int i=1;i<N;i++)
{
g[cnt[i]][i]=power(g[cnt[i]][i],p);
if(g[cnt[i]][i]) ig[i]=power(g[cnt[i]][i],mod-2);
if(!check(i)) g[cnt[i]][i]=0;
}
}
void FWT_or(int *a,int f)
{
for(int i=1;i<N;i<<=1)
for(int j=0;j<N;j+=(i<<1))
for(int k=0;k<i;k++)
a[i+j+k]=((f==1)?pls(a[i+j+k],a[j+k]):dec(a[i+j+k],a[j+k]));
}
void work()
{
for(int i=1;i<=n;i++) FWT_or(g[i],1);
f[0][0]=1;FWT_or(f[0],1);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
for(int r=0;r<N;r++)
f[i][r]=pls(f[i][r],(ll)g[j][r]*f[i-j][r]%mod);
FWT_or(f[i],-1);
for(int r=0;r<N;r++) f[i][r]=(cnt[r]==i?(ll)f[i][r]*ig[r]%mod:0);
if(i^n) FWT_or(f[i],1);
}
printf("%d\n",f[n][N-1]);
}
int main()
{
input();
work();
return 0;
}