题解:P11714 [清华集训 2014] 主旋律
给一个
- DAG 子图计数
直接定义
于是转移时乘上容斥系数即可,得到
-
强连通子图计数(主旋律)
首先【强连通子图
= 总子图- 非强连通子图】。考虑这跟DAG计数的关系,有重要性质,就是所有非强连通子图在SCC缩点后构成点数大于
1 的DAG。那么直接定义
f_S 表示点集为S 的强连通子图数量,并令G_S 表示点集为S 的子图总数。总子图有
G_S 个,对于非强连通子图,我们在其缩点后的DAG上进行刻画,根据套路找到所有入度为0 的SCC,那么相当于要拎出来一个点集T 让其构成一个个独立的SCC,定义这个方案数为g_T ,那么不难发现剩下的部分可以任意组合,都满足条件。根据之前的分析,此时需要进行容斥,即选出的SCC组成的集合
T^\prime 会被按照多种子集划分统计,仿照DAG计数,我们知道这个容斥系数应为(-1)^{|T^\prime|-1} ,不难发现这个要在g 的计算中被完成。那么f_{S}=G_S-\sum_{T\subseteq S\wedge T\ne \empty}g_{T}2^{\mathrm{cross}(T,S\setminus T)}G_{S\setminus T} 接下来考虑
g 的计算。先考虑整个
S 都是一个SCC,这种情况贡献是f_S 。对于多个独立的SCC,拎一个出来,记其点集为
T ,那么其个数就是f_{T} ,剩下的就是g_{S\setminus T} ,并且每新增一个 SCC就要乘上-1 来实现容斥系数,于是转移应该是-f_{T}g_{S\setminus T} 状物。但是这样会有问题,就是你选择SCC的顺序会造成影响,于是进行钦定顺序,假设钦定一个点x 表示选择的SCCT 包含x ,那么由于包含x 的SCC是唯一的,于是就完成了顺序钦定,考虑定义一个SCCT 的代表为\mathrm{lowbit}(T) ,于是得到g_{S}=f_{S}-\sum_{T\subset S\wedge \mathrm{lowbit}(T)=\mathrm{lowbit}(S)}f_{T}g_{S\setminus T}
还没完,对于
那么我们得到了
我们算法的瓶颈在于计算
于是可以在枚举子集时随便加一个
最终时间复杂度
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=15,M=215,mod=1e9+7;
typedef long long ll;
inline int Mod(int x) { return x<0 ? x+mod : (x>=mod ? x-mod : x); }
inline void adm(int &x,int y) { x=Mod(x+y); }
inline void dec(int &x,int y) { x=Mod(x-y); }
inline int lowbit(int x) { return x & (-x); }
int n,m;
bool e[N][N];
int in[N],out[N];
int pw2[M],ppcnt[1<<N],bit[1<<N];
int G[1<<N];
int cross[1<<N];
int f[1<<N],g[1<<N];
inline void print(int S) { for (int i=0;i<n;i++) cout << ((S>>i)&1); }
int main()
{
cin >> n >> m;
for (int i=1;i<=m;i++)
{
int u,v;
cin >> u >> v;
u--,v--;
e[u][v]=true;
in[v]|=(1<<u);
out[u]|=(1<<v);
}
pw2[0]=1;
for (int i=1;i<=m;i++) pw2[i]=(ll)pw2[i-1]*2%mod;
for (int i=1;i<(1<<n);i++) ppcnt[i]=ppcnt[i>>1]+(i&1);
for (int i=0;i<n;i++) bit[1<<i]=i;
for (int S=1;S<(1<<n);S++)
{
int u=bit[lowbit(S)];
G[S]=G[S^(1<<u)]+ppcnt[in[u]&S]+ppcnt[out[u]&S];
}
g[0]=-1;
for (int S=0;S<(1<<n);S++)
{
memset(cross,0,sizeof(cross));
for (int T=S&(S-1);T;T=(T-1)&S)
{
int u=bit[lowbit(S-T)];
cross[T]=cross[T|(1<<u)]+ppcnt[in[u]&T]-ppcnt[out[u]&(S-T)];
}
for (int T=S&(S-1);T;T=(T-1)&S)
{
if (lowbit(T)==lowbit(S))
{
dec(g[S],(ll)f[T]*g[S-T]%mod);
}
}
f[S]=pw2[G[S]];
for (int T=S;T;T=(T-1)&S) dec(f[S],(ll)g[T]*pw2[cross[T]]%mod*pw2[G[S-T]]%mod);
adm(g[S],f[S]);
}
cout << f[(1<<n)-1] << "\n";
return 0;
}