传送门:bzoj3812
题解
神仙容斥给跪了orz。
设 f [ S ] f[S] f[S]表示点集 S S S强连通生成子图的方案数(即答案), s u m [ S ] sum[S] sum[S]表示点集 S S S中的有向边数,考虑容斥:用总方案数 2 s u m [ S ] 2^{sum[S]} 2sum[S]减去所有非强连通图的方案数。
枚举非强连通图的方法很巧妙:
若缩点后入度为0的 s c c scc scc点集不为全集,则图不强连通。那么可以枚举缩点后所有入度为0的 s c c scc scc的点集 T T T,设 g ′ [ T ] g'[T] g′[T]表示上述点集为 T T T的方案数(即一些 s c c scc scc,且 s c c scc scc间无连边)。设 S S S中指向子集 T T T的边数量为 w [ S ] [ T ] w[S][T] w[S][T],贡献为 2 s u m [ S ] − w [ S ] [ T ] × g ′ [ T ] 2^{sum[S]-w[S][T]}\times g'[T] 2sum[S]−w[S][T]×g′[T]。
但这样不能保证
S
−
T
S-T
S−T中没有入度为0的
s
c
c
scc
scc,所以需要进一步容斥,奇数个连通块容斥系数为正,偶数个为负(类似于至少有多少个),设
g
[
T
]
g[T]
g[T]表示点集
T
T
T形成奇数个连通块的方案数-点集
T
T
T形成偶数个连通块的方案数。
g
[
S
]
=
−
∑
T
⊂
S
,
l
o
w
b
i
t
(
S
)
∈
T
f
[
T
]
g
[
S
−
T
]
g[S]=-\sum \limits_{T\subset S,lowbit(S)\in T}f[T]g[S-T]
g[S]=−T⊂S,lowbit(S)∈T∑f[T]g[S−T]
综上
f
[
S
]
=
2
s
u
m
[
S
]
−
∑
T
⊂
S
2
s
u
m
[
S
]
−
w
[
S
]
[
T
]
g
[
T
]
f[S]=2^{sum[S]}-\sum\limits_{T\subset S}2^{sum[S]-w[S][T]}g[T]
f[S]=2sum[S]−T⊂S∑2sum[S]−w[S][T]g[T]
p.s.需要在同步更新完
f
[
S
]
,
g
[
S
]
f[S],g[S]
f[S],g[S]将
f
[
S
]
f[S]
f[S]计入
g
[
S
]
g[S]
g[S](实际上在转移的时候,
g
[
S
]
g[S]
g[S]是没有算
s
c
c
scc
scc为全集的情况的)
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=15,M=(1<<15)+10,mod=1e9+7;
int S,n,m,in[M],ot[M],bin[300],stk[M],top;
int f[M],g[M],sum[M],w[M],sz[M];
inline int ad(int x,int y){x+=y;return x>=mod?x-mod:x;}
inline int dc(int x,int y){x-=y;return x<0?x+mod:x;}
int main(){
int i,j,k,x,y;bin[0]=1;
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i){
scanf("%d%d",&x,&y);x--;y--;
in[1<<y]|=1<<x;ot[1<<x]|=1<<y;
bin[i]=ad(bin[i-1],bin[i-1]);
}S=bin[n];
for(i=1;i<S;++i){
j=i&(-i);x=i^j;sz[i]=sz[x]+1;
sum[i]=sum[x]+sz[in[j]&i]+sz[ot[j]&i];
f[i]=bin[sum[i]];top=0;
for(j=i;j;j=i&(j-1)) stk[++top]=j;
for(;top;){k=stk[top--];w[k]=w[k^(k&(-k))]+sz[in[k&(-k)]&i];}
for(j=x;j;j=x&(j-1)) g[i]=dc(g[i],(ll)f[i^j]*g[j]%mod);
for(j=i;j;j=i&(j-1)) f[i]=dc(f[i],(ll)bin[sum[i]-w[j]]*g[j]%mod);
g[i]=ad(g[i],f[i]);
}
printf("%d",f[S-1]);
return 0;
}