f
[
S
]
f[S]
f[S] 表示原图的点集
S
S
S 的导出子图中,有多少个边子集能强连通点集
S
S
S
g
[
S
]
g[S]
g[S] 表示将原图的点集
S
S
S 分割成一些非空子集,每个点子集分别用其内部的边将其强连通的方案数,分成的子集个数为奇数时计入
g
[
S
]
g[S]
g[S] ,子集个数为偶数时从
g
[
S
]
g[S]
g[S] 里扣掉
如果没有强连通的要求,那么显然
f
[
S
]
=
2
c
n
t
[
S
]
f[S]=2^{cnt[S]}
f[S]=2cnt[S] (
c
n
t
[
S
]
cnt[S]
cnt[S] 为点集
S
S
S 的导出子图边数)
对一个状态
S
S
S 处理时,先完成
g
g
g 的部分转移
g
[
S
]
=
−
∑
T
⊂
S
,
0
<
∣
T
∣
<
∣
S
∣
,
i
d
∈
T
f
[
T
]
g
[
S
−
T
]
g[S]=-\sum_{T\subset S,0<|T|<|S|,id\in T}f[T]g[S-T]
g[S]=−T⊂S,0<∣T∣<∣S∣,id∈T∑f[T]g[S−T]
注意上面的
i
d
id
id 为
S
S
S 内首个元素,限制
i
d
∈
T
id\in T
id∈T 是为了避免重复计数
这时候
g
[
S
]
g[S]
g[S]还没记录 S 只分成一个子集即本身的方案
然后考虑
f
[
S
]
f[S]
f[S] ,对于满足
T
⊂
S
,
∣
T
∣
>
0
T\subset S,|T|>0
T⊂S,∣T∣>0 的任意集合
T
T
T ,我们需要扣除掉入度为
0
0
0 的强连通分量包含的点集为
T
T
T 的方案数
这扣掉的方案数好像就是
点
集
T
划
分
成
互
相
独
立
的
强
连
通
图
的
方
案
数
×
2
c
n
t
2
[
S
]
[
T
]
点集T划分成互相独立的强连通图的方案数\times2^{cnt_2[S][T]}
点集T划分成互相独立的强连通图的方案数×2cnt2[S][T]
其中
c
n
t
2
[
S
]
[
T
]
cnt_2[S][T]
cnt2[S][T] 表示两端都在点集
S
S
S 中且指向点集
T
T
T 外的边数 ,怎么求各凭本事
然后我们发现上面其实是有问题的,因为我们没办法知道点集
S
−
T
S-T
S−T 内是否有入度为
0
0
0 的强连通分量 ,会算重复
于是我们考虑容斥
即
T
T
T 划分成奇数个互相独立的强连通图时有正贡献,否则负贡献
转移方程出来了
f
[
S
]
=
2
c
n
t
[
i
]
−
∑
T
∈
S
,
∣
T
∣
>
0
2
c
n
t
2
[
S
]
[
T
]
g
[
T
]
f[S]=2^{cnt[i]}-\sum_{T\in S,|T|>0}2^{cnt_2[S][T]}g[T]
f[S]=2cnt[i]−T∈S,∣T∣>0∑2cnt2[S][T]g[T]
注意,这时候
g
[
S
]
g[S]
g[S] 还没记录
S
S
S 只分成一个子集即本身的方案,所以这时还要将
g
[
S
]
g[S]
g[S] 加上
f
[
S
]
f[S]
f[S]
最后答案就是
f
[
全
集
]
f[全集]
f[全集]
Code
#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>inlineintread(){int res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')
res =(res <<3)+(res <<1)+(c -48);return bo ?~res +1: res;}constint N =19, M = N * N, C =(1<<15)+5, ZZQ =1e9+7;int n, m, Cm, pw[M], f[C], g[C], cnt[C], cnt2[N][C], sze[C], tot, st[C], rp[C];bool G[N][N];intmain(){
n =read(); m =read();
pw[0]=1;for(int i =1; i <= m; i++)
pw[i]=2* pw[i -1]% ZZQ;while(m--) G[read()][read()]=1;
Cm =1<< n;for(int S =1; S < Cm; S++)for(int u =1; u <= n; u++)if((S >> u -1)&1)for(int v =1; v <= n; v++)if(((S >> v -1)&1)&& G[u][v])
cnt[S]++, cnt2[v][S]++;for(int i =1; i <= n; i++) rp[1<< i -1]= i;for(int S =1; S < Cm; S++){if(S ==(S &-S)){
f[S]= g[S]=1;continue;}
tot =0;int id = rp[S &-S];for(int T =(S -1)& S; T; T =(T -1)& S)
st[++tot]= T;for(int i = tot; i >=1; i--){int s = st[i], t = s &-s;
sze[s]= sze[s ^ t]+ cnt2[rp[t]][S];}
f[S]= pw[cnt[S]];for(int T =(S -1)& S; T; T =(T -1)& S)if((T >> id -1)&1)
g[S]=(g[S]-1ll* f[T]* g[S ^ T]% ZZQ + ZZQ)% ZZQ;for(int T = S; T; T =(T -1)& S)
f[S]=(f[S]-1ll* g[T]* pw[sze[S ^ T]]% ZZQ + ZZQ)% ZZQ;
g[S]=(g[S]+ f[S])% ZZQ;}
std::cout << f[Cm -1]<< std::endl;return0;}