题意:
问n个点的生成子图是强连通的方案数。
题解:
神题,纠结了我几天。
先贴题解链orz
这题用容斥,即总的减不合法的。
首先一种暴力点的做法,因为不是强连通的话缩点后一定是>1个点的
DAG
D
A
G
,所以可以枚举强连通分量,然后算出当前点集的生成子图的方案数
F(S)
F
(
S
)
因为
DAG
D
A
G
中一定有一些点出度为0,所以可以枚举那些点。
F(S)=∑T∈S,T≠ϕ(−1)|T|−12ways(S−T,T)F(S−T)(4)
(4)
F
(
S
)
=
∑
T
∈
S
,
T
≠
ϕ
(
−
1
)
|
T
|
−
1
2
w
a
y
s
(
S
−
T
,
T
)
F
(
S
−
T
)
其中 ways(S,T) w a y s ( S , T ) 表示点集 S S 到点集的边数。
因不能保证剩下的点中没有出度为0的点,且 ∑T∈S,T≠ϕ(−1)|T|−1=1 ∑ T ∈ S , T ≠ ϕ ( − 1 ) | T | − 1 = 1 ,所以可以容斥。
然而这么做肯定会T。
其实枚举强连通分量只是为了得到容斥系数,换个角度想,我们可以算出一个东西 g(S) g ( S )
表示将点集 S S 分成奇数个强连通分量的方案数-偶数个强连通分量的方案数。
再设是点集 S S 的生成子图强连通方案数(就是题目要求的东西)
这个挺好理解不解释,那个减号是因为多了一个强联通分量要取反。
然后推 f f
其中 h(S) h ( S ) 表示点集 S S 的边数。
因为已经容斥过了,这个时候就不是严格枚举点,所以剩下的点就可以任意连边没有限制。
好像 f,g f , g 会互相调用,其实当 S=T S = T 时 g(S) g ( S ) 不包含这 f(S) f ( S ) 部分。
code:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
const int mod=1000000007;
int n,m;
LL pow[225],f[1<<15],g[1<<15],h[1<<15],p[1<<15];
int bitcnt[1<<15],e_in[1<<15],e_out[1<<15];
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d %d",&x,&y);
x=1<<(x-1);y=1<<(y-1);
e_in[y]|=x;e_out[x]|=y;
}
pow[0]=1;
for(int i=1;i<=n*n;i++) pow[i]=pow[i-1]*2%mod;
bitcnt[0]=0;
for(int i=1;i<(1<<n);i++)
bitcnt[i]=bitcnt[i-(i&-i)]+1;
for(int s=1;s<(1<<n);s++)
{
int u=s&-s,t=s^u;
for(int i=t;i;i=(i-1)&t)
g[s]=(g[s]-f[s^i]*g[i])%mod;
h[s]=h[t]+bitcnt[e_in[u]&t]+bitcnt[e_out[u]&t];
f[s]=pow[h[s]];
for(int i=s;i;i=(i-1)&s)
{
if(i!=s)
{
u=(i^s)&-(i^s);
p[i]=p[i^u]+bitcnt[e_out[u]&i]-bitcnt[e_in[u]&(s^i)];
}
else p[i]=0;
f[s]=(f[s]-pow[p[i]+h[s^i]]*g[i])%mod;
}
g[s]=(g[s]+f[s])%mod;
}
printf("%lld",(f[(1<<n)-1]+mod)%mod);
}