题解 BZOJ3812【[清华集训 2014] 主旋律】

problem

给定一个 \(n\) 个点的有向图,问这个图有多少个强连通子图。保证 \(n\leq 15\)

preparing

一个有向图有 \(n\) 个点,求它的 DAG 子图数量。\(n\leq 15\)

解:令 \(f[S]\) 表示点集 \(S\) 有多少个 DAG 子图。枚举这个 DAG 中入度为 0 的部分,然后递归下去。由于不能一次枚举完所有入度为 0 的点,我们对着这些点容斥。

\[f[S]=\sum_{T\subseteq S,T\neq\varnothing}(-1)^{|T|+1}2^{ways(T,S-T)}f[S-T]. \]

solution

一个非强连通的子图,缩点后是一个至少两个点的 DAG(这个 DAG 可以不连通)。

对于点集 \(S\),考虑强连通子图 = 所有图 - 非强连通子图。

令强连通子图的数量为 \(f[S]\),枚举 \(T\subset S\),使 \(T\) 是一个强连通分量,\(S-T\) 就随意,而且强制规定 \(T\) 在缩点图的入度为零。

\[f[S]=all[S]-\sum_{T\subset S}2^{ways(T,S-T)}f[T]all[S-T]. \]

  • \(all[S]\) 表示所有图的数量。
  • \(ways(S,T)\) 表示 \(\sum_{u\in S,v\in T}g[u][v]\)。注意到 \(S\cap T=\varnothing\),它的复杂度顶满是 \(O(3^nn^2)\)

错误的。考虑 \(1\to 3,2\to 3\) 会被算两次。

考虑问题在哪里呢?枚举 \(T\) 的时候,我们没有钦定 \(T\) 一定是所有的入度为 \(0\) 的点。我们对着 \(T\) 容斥:

  • \(T\) 划分为若干个强连通分量,奇数个的贡献是 1,偶数个的贡献是 -1;
  • \(g[T]\) 表示所有划分方案的和。

\(f[T]\) 换成 \(g[T]\) 就行了。

\[f[S]=all[S]-\sum_{T\subset S}2^{ways(T,S-T)}g[T]all[S-T]. \]

怎么算 \(g[S]\)

  • 枚举一个 \(T\subset S\),然后划分成 \(-f[T]g[S-T]\)
  • 细节一:一种好的做法是令 \(g[0]=0\),然后特判 \(T\) 是整一块强连通分量的情况。
  • 细节二:为了避免判重等奇奇怪怪的问题,我们钦定一个 \(x\in S\),然后枚举 \(x\in T\land T\subset S\)

\[g[S]=f[S]-\sum_{x\in T\subset S}f[T]g[S-T]. \]

code

细节巨大

点击查看代码

debug:

2 1
1 2
//输出 0

先算 \(h\)(忽略单独块的影响),然后 \(f\) 枚举全部,最后直接 \(h+=f\)

理解:少掉的这个 \(h\),它就只是单纯的容斥,把空图干掉,除此之外没有实际意义。

#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
const int P=1e9+7;
LL mod(LL x){return (x%P+P)%P;}
void red(LL &x){x=mod(x);}
int lowbit(int S){return S&-S;}
int getbit(int k){return 1<<k;}
bool conbit(int S,int k){return S&getbit(k);}
int n,m,g[15][15],lg[1<<15];
LL f[1<<15],h[1<<15],all[1<<15],pow2[100010];
int calc(int S,int T){//S\cap T=\varnothing
	int ans=0;
//	debug("S=%d,T=%d\n",S,T);
	for(int i=S;i;i-=lowbit(i)){
		for(int j=T;j;j-=lowbit(j)){
//			debug("ans+=g[%d][%d]\n",lg[lowbit(i)],lg[lowbit(j)]);
			ans+=g[lg[lowbit(i)]][lg[lowbit(j)]];
		}
	}
	return ans;
}
LL dp(){
	memset(f,0,sizeof f);
	memset(h,0,sizeof h);
	all[0]=0;
	for(int S=1;S<1<<n;S++){
		red(all[S]=all[S^lowbit(S)]+calc(lowbit(S),S^lowbit(S))+calc(S^lowbit(S),lowbit(S)));
		for(int St=S^lowbit(S),T=St;;T=(T-1)&St){red(h[S]-=f[T|lowbit(S)]*h[St^T]);debug("T=%d,h[%d]-=f[%d]*h[%d](%d*%d)\n",T,S,T|lowbit(S),St^T,f[T|lowbit(S)],h[St^T]);if(!T) break;}
		debug("calc: h[%d]=%lld\n",S,h[S]);
		f[S]=pow2[all[S]];
		for(int T=S;T;T=(T-1)&S) red(f[S]-=pow2[calc(T,S^T)+all[S^T]]*h[T]),debug("T=%d,f[%d]-=%lld*%lld\n",T,S,pow2[calc(T,S^T)+all[S^T]],h[T]);
		red(h[S]+=f[S]);
		debug("all[%d]=%lld,f[%d]=%lld,h[%d]=%lld\n",S,all[S],S,f[S],S,h[S]);
	}
	return f[getbit(n)-1];
}
int main(){
//	#ifdef LOCAL
//	 	freopen("input.in","r",stdin);
//	#endif
	for(int i=pow2[0]=1;i<=1e5;i++) red(pow2[i]=pow2[i-1]*2);
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++) lg[getbit(i)]=i;
	for(int i=1,u,v;i<=m;i++) scanf("%d%d",&u,&v),g[u-1][v-1]=1;
	printf("%lld\n",dp());
	return 0;
}

/*
calc: h[3]=-1+x
f[3]=2-3-h[3]=-1+1-x
*/

posted @ 2023-01-05 17:14  caijianhong  阅读(188)  评论(0编辑  收藏  举报