思路
对于一个 DAG,把所有边反向后也是 DAG。即 步做出一个 DAG,就可以 步做出对应的另一个。所以答案为 DAG 数乘 。
状压 表示 的导出子图的 DAG 数。枚举 的子集 ,认为 是无入度点集合,则 中无边,变为 的子问题。但 可能是真正的无入度点集合的子集,尝试容斥。赋 的容斥系数,对于真正的无入度点集合 ,。这个也叫 DAG 容斥或零度点容斥?
设 表示 内是否有边。然后 枚举子集即可:。
进一步,可以看成从空集开始每次加上一个集合(有序),集合带有 的系数。对于集合幂级数 , 且 ,设 ,等价于求 在全集的系数。也即求集合幂级数 的 逆,那逆着子集卷积做即可。复杂度 ,还是能快一点的。
code
int n,m;
int e[1<<maxn],f[1<<maxn],g[1<<maxn];
void fmt(int *a,int n,int w){
for(int i=0;i<n;i++){
if(w==1){
for(int s=0;s<(1<<n);s++)if(s&(1<<i))(a[s]+=a[s^(1<<i)])%=mod;
}
else{
for(int s=0;s<(1<<n);s++)if(s&(1<<i))(a[s]+=mod-a[s^(1<<i)])%=mod;
}
}
}
int ff[maxn+1][1<<maxn],hh[maxn+1];
void xorni(int *a,int *b,int n){//g=1/(1-f)
for(int i=0;i<=n;i++){
for(int s=0;s<(1<<n);s++)ff[i][s]=0;
}
for(int s=0;s<(1<<n);s++)ff[__builtin_popcount(s)][s]=a[s];
for(int i=0;i<=n;i++)fmt(ff[i],n,1);
for(int s=0;s<(1<<n);s++){
ff[0][s]=mod+1-ff[0][s];for(int i=1;i<=n;i++)ff[i][s]=mod-ff[i][s];
int nif=ksm(ff[0][s]);
for(int i=0;i<=n;i++){
hh[i]=1;
for(int j=1;j<=i;j++)(hh[i]+=mod-ff[j][s]*hh[i-j]%mod)%=mod;
hh[i]=hh[i]*nif%mod;
}
for(int i=0;i<=n;i++)ff[i][s]=hh[i];
}
for(int i=0;i<=n;i++)fmt(ff[i],n,-1);
for(int s=0;s<(1<<n);s++)b[s]=ff[__builtin_popcount(s)][s];
}
void work(){
n=read();m=read();
for(int i=1;i<=m;i++){
int u=read()-1,v=read()-1;
e[(1<<u)|(1<<v)]++;
}
fmt(e,n,1);
f[0]=0;for(int s=1;s<(1<<n);s++){
if(e[s])f[s]=0;
else f[s]=((__builtin_popcount(s)+1)&1)?-1:1;
}
xorni(f,g,n);
printf("%lld\n",g[(1<<n)-1]*m%mod*(mod+1)/2%mod);
}