Description
给出一个无向连通图,求给每条边定向后是DAG(有向无环图)的方案数,两种方案不同当且仅当存在一条边它们的方向不同。
Solution
设
f
S
f_S
fS表示集合s的点在DAG上的方案数,转移时枚举一个独立集
T
T
T表示度数为0的点,大概转移是这样:
f
S
=
∑
T
⊂
S
f
S
−
T
(
−
1
)
∣
T
∣
−
1
f_S=\sum_{T\subset S} f_{S-T}(-1)^{|T|-1}
fS=∑T⊂SfS−T(−1)∣T∣−1。
直接枚举是
3
n
3^n
3n的,子集卷积一下即可。
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
#define rep(i,x) for(int i=ls[x];i;i=nx[i])
using namespace std;
typedef long long ll;
const int N=22,mo=1e9+7;
void inc(int &x,int y){
x=x+y>=mo?x+y-mo:x+y;
}
void fwt(int *a,int n,int sig){
for(int m=2;m<=n;m<<=1)
for(int i=0,hf=m>>1;i<n;i+=m)
fo(j,i,i+hf-1) inc(a[j+hf],sig>0?a[j]:mo-a[j]);
}
int a[N][N],d[N];
int f[N][1<<20],g[N][1<<20];
int cn[1<<20];
int main()
{
freopen("jump.in","r",stdin);
freopen("jump.out","w",stdout);
int n,m;
scanf("%d %d",&n,&m);
fo(i,1,m){
int u,v;
scanf("%d %d",&u,&v);
a[u][v]=a[v][u]=1;
}
fo(s,1,(1<<n)-1){
cn[s]=cn[s-(s&-s)]+1;
int now=1,tot=0;
fo(i,1,n) if(s&(1<<i-1)) d[++tot]=i;
fo(i,1,tot-1){
fo(j,i+1,tot) if(a[d[i]][d[j]]) {now=0;break;}
if(!now) break;
}
g[cn[s]][s]=(now*(cn[s]&1?1:-1)+mo)%mo;
}
fo(i,0,n-1) fwt(g[i],1<<n,1);
f[0][0]=1;
fo(i,1,n){
fwt(f[i-1],1<<n,1);
fo(j,0,i-1)
fo(k,0,(1<<n)-1) inc(f[i][k],(ll)f[j][k]*g[i-j][k]%mo);
fwt(f[i],1<<n,-1);
fo(j,0,(1<<n)-1) if(cn[j]!=i) f[i][j]=0;
}
printf("%d\n",f[n][(1<<n)-1]);
}