loj2340 WC2018 州区划分 状压dp+FWT
作者:互联网
Description
题面到处都有系列。。
Solution
FMT是啥,能吃吗
首先考虑怎么判合法子图(也就是欧拉回路),我们n2*2n枚举点然后统计度数就可以了
那么一个比较显然的dp就是设f[S]表示二进制状态为S的所有答案,g[S]表示S这个集合分成一份的贡献
我们枚举S的子集转移即可,这样做是O(3n)的
考虑把柿子写出来,那么就是fS=T⊂S∑fT⋅gS∖T
这实际上就是一个子集卷积,只不过这个有自己卷自己的部分。
注意到T⊂S∑fT⋅gS∖T=U⊂S,V⊂S,U∪V=S,U∩V=∅∑fU⋅gV=U⊂S,V⊂S,∣U∣+∣V∣=∣S∣,U∪V=S∑fU⋅gV
这里的转化十分的巧妙
于是我们设fi,S表示二进制中i位是1,状态为S的答案,我们对i做卷积,然后对S做或卷积就可以了
于是复杂度就是O(n2⋅2n)的了,我写的代码要卡一卡常数。。
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (register int i=st;i<=ed;++i)
typedef long long LL;
const int MOD=998244353;
const int N=2197152;
LL f[22][N],g[22][N],s[N],inv[N];
int rc[22][22],fa[22],d[22],w[22],c[N];
int n,m,p,lim;
void upd(LL &x,LL y) {
x+=y; (x>=MOD)?(x-=MOD):0;
}
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void FWT(LL *a,int f) {
for (register int i=1;i<lim;i<<=1) {
for (register int j=0;j<lim;j+=(i<<1)) {
for (register int k=0;k<i;++k) {
if (f==1) upd(a[j+k+i],a[j+k]);
else a[j+k+i]=(a[j+k+i]+MOD-a[j+k])%MOD;
}
}
}
}
LL ksm(LL x,LL dep) {
LL res=1;
for (;dep;dep>>=1) {
(dep&1)?(res=res*x%MOD):0;
x=x*x%MOD;
}
return res;
}
int find(int x) {
return !fa[x]?x:(fa[x]=find(fa[x]));
}
bool merge(int x,int y) {
x=find(x),y=find(y);
if (x==y) return false;
return 1|(fa[x]=y);
}
bool check(int S) {
rep(i,1,n) fa[i]=d[i]=0;
int cnt=1;
rep(i,1,n) if (S>>(i-1)&1) {
s[S]=(s[S]+w[i])%MOD;
rep(j,i+1,n) if (S>>(j-1)&1) {
if (!rc[i][j]) continue;
++d[i],++d[j];
cnt+=merge(i,j);
}
}
inv[S]=ksm(ksm(s[S],MOD-2),p);
rep(i,1,n) if (S>>(i-1)&1) {
if (d[i]&1) return false;
cnt--;
}
return !cnt;
}
int main(void) {
freopen("data.in","r",stdin);
freopen("myp.out","w",stdout);
n=read(),m=read(),p=read();
lim=(1<<n)-1;
rep(i,1,m) {
int x=read(),y=read();
rc[x][y]=rc[y][x]=1;
}
rep(i,1,n) w[i]=read();
rep(i,0,lim) c[i]=c[i>>1]+(i&1);
rep(i,1,lim) if (!check(i)) {
// printf("%d\n", i);
g[c[i]][i]=ksm(s[i],p);
}
f[0][0]=1;
rep(i,0,n-1) f[1][1<<i]=(g[1][1<<i]!=0);
rep(i,1,n) FWT(g[i],1);
FWT(f[0],1);
FWT(f[1],1);
rep(i,2,n) {
rep(j,1,i) rep(k,0,lim) {
upd(f[i][k],g[j][k]*f[i-j][k]%MOD);
}
FWT(f[i],-1);
rep(k,0,lim) {
if (c[k]!=i) f[i][k]=0;
else f[i][k]=f[i][k]*inv[k]%MOD;
}
FWT(f[i],1);
// for (int j=0;j<=lim;++j) printf("%lld ", f[i][j]); puts("");
}
FWT(f[n],-1);
printf("%lld\n", f[n][lim]);
return 0;
}
标签:subset,州区,ch,return,int,loj2340,rep,状压,MOD 来源: https://blog.csdn.net/jpwang8/article/details/87948668