【清华集训2014】主旋律

【清华集训2014】主旋律

题目大意

给定一张\(n\)个点\(m\)条边的无向图,保证该图整个图为一个强联通分量,保证无重边自环。
现在需要求出:有多少种删边方案,使得删完边后,整个图依旧是一个强联通分量。
数据范围:\(n\leq 15 , m\leq n(n-1)\)

题解

容斥题是真的鸡贼......,这\(TMD\)是人能想出来的?
希望下次自己再来看这篇题解的时候还能看懂吧......
规定\(E[U,V]\)表示出度在\(U\)中,入度在\(V\)中的所有边的集合,其中\(U\)\(V\)为点集。
对于一个点集\(S\),我们随便连边,然后缩点。
那么缩完点后,形成的一定是一个\(DAG\)
我们枚举缩完点后,构成出度为\(0\)\(scc\)的点是哪些,设这个点集为\(T\)
那么每一种\(T\)集合都可以与原来的某些连边方案对应起来。
我们确定完\(T\)后,\(S-T\)中的点先让它随便连,然后\(S-T\)连向\(T\)中的边也可以随便连。
我们可以暂时得到一个式子:

\[2^{E[S,S]} = \sum_{T\subset S,T \neq null} g_T *2^{E[S-T,S-T]}*2^{E[S-T,T]} \]

\(f_S\)表示连完\(E[S,S]\)中的边后,\(S\)整张图为一个强联通分量的方案数,即题目所求。
上式中\(g_T\)表示把\(T\)中的点连成若干强联通分量的方案数。
显然,直接\(g_T = \sum_{s_1,s_2...,s_k} \prod_{k} f_s\)是不对的。
因为由于\(E[S-T,S-T]\)中的边也是随便连的,所以还有可能会形成其他的出度为\(0\)\(scc\)
所以这里应该是存在一个容斥系数的,即:

\[g_T = \sum_{s_1,s_2,...,s_k} coef_k*f_{s_1}f_{s_2}...f_{s_k} \]

我们令这个式子为\(g_T\)的定义式。
考虑一下这个容斥系数\(coef\)到底是什么。
我们设\(r_k\)表示划分成\(k\)个出度为\(0\)\(scc\)的方案数应该要被算几遍,显然\(r_k=1\)
然后我们来推容斥系数\(coef\)应该设为多少才能够使得计算出来的结果与\(r_k\)等效。
对于被划分成\(k\)个出度为\(0\)\(scc\)的方案,
它在划分为\(k-1\)个中被算了\(\binom{k}{k-1}\)次,在划分为\(k-2\)个中被算了\(\binom{k}{k-2}\)次......
所以我们有:

\[r_k = \sum_{j=1}^k\binom{k}{j} coef_j \]

显然是一个二项式反演,结合\(r_k=1\)与二项式定理我们可以得到:

\[coef_k = \sum_{j=1}^k (-1)^{k-j}\binom{k}{j} r_k = ((-1)+1)^k - \binom{k}{0}(-1)^k =(-1)^{k+1} \]

所以我们得到了容斥系数\(coef_k = (-1)^{k+1}\),带入之前\(g_T\)的定义式中:

\[g_T = \sum_{s_1,s_2,...,s_k} (-1)^{k+1} coef_k*f_{s_1}f_{s_2}...f_{s_k} \]

根据容斥系数可以看出,\(g_{null} = -1\)
现在我们终于弄清楚\(g_T\)究竟应该是什么了!
到此为止,所有的准备工作已经就绪,下面开始求答案。
回到这个式子:

\[2^{E[S,S]} = \sum_{T\subset S , T\neq null} g_T *2^{E[S-T,S-T]}*2^{E[S-T,T]} \]

注意到这个式子中是没有\(f\)数组的,所以:

\[g_S = 2^{E[S,S]} - \sum_{T\subsetneqq S , T\neq null} g_T *2^{S-T,S-T} * 2^{E[S-T,T]} \]

所以顺次把\(g\)数组全部推出来即可。
然后我们来研究一下\(g\)的定义式:

\[g_T = \sum_{s_1,s_2,...,s_k} (-1)^{k+1} coef_k*f_{s_1}f_{s_2}...f_{s_k} \]

注意到,每加入一个\(scc\),容斥系数会乘上一个\(-1\)
由于是无序关系,所以我们可以枚举最小编号所在的强联通分量\(s_1\),然后就可以得到:

\[g_T = (-1) * \sum_{s_1 \subset T , s_1 \neq null} f_{s_1} g_{T-s_1} \]

类似之前求\(g\)的技巧,移项可以得到:

\[f_T = g_T + \sum_{s_1 \subsetneqq T, s_1 \neq null} f_{s_1} g_{T-s_1} \]

所以说只要解决了\(g\),就可以顺次推出\(f\),而\(g\)我们已经会求了,所以就做完了。
累死我了.......
不玩了不玩了,再以不玩这种毒瘤题了......

实现代码

#include<bits/stdc++.h>
#define IL inline
#define mod 1000000007
using namespace std ;

int lk[20][20],Inside[(1<<17)],Edge[(1<<17)][20],n,m,pw[233333],pos[(1<<17)] ;
int f[(1<<17)] , g[(1<<17)] , num[(1<<17)] , mx ; 

IL void Pre() {
	for(int i = 1,u,v; i <= m; i ++) cin >> u >> v , lk[u][v] ++ ;
	mx = (1 << n) ; 
	for(int S = 0; S < mx; S ++)
		for(int i = 1; i <= n; i ++)
			for(int j = 1; j <= n; j ++)
				if((S&(1<<(i-1))) && (S&(1<<(j-1)))) Inside[S] += lk[i][j] ;
	for(int S = 0; S < mx; S ++)
		for(int v = 1; v <= n; v ++) if(S & (1 << (v - 1))) continue ;
			else for(int u = 1; u <= n; u ++)
					 if(S & (1 << (u - 1))) Edge[S][v] += lk[u][v] ;
	for(int S = 0; S < mx; S ++) {
		for(int v = 1; v <= n; v ++) if(S & (1 << (v - 1))) pos[S] = v ;
	}
	pw[0] = 1 ;
	for(int i = 1; i <= m; i ++) pw[i] = (pw[i - 1] << 1) % mod ; return ; 
}

IL void add(int &x , int y) {x += y ; if(x >= mod) x -= mod ; }

int main() {
	freopen("scon.in","r",stdin) ;
	freopen("scon.out","w",stdout) ;
	cin >> n >> m ;
	Pre() ; 
	g[0] = mod - 1 ;
	for(int S = 1; S < mx; S ++) {
		g[S] = pw[Inside[S]] ;
		for(int T = S,tmp; T ; T = (T - 1) & S) {
			num[T] = 0 ; tmp = T ; while(tmp) add(num[T] , Edge[S ^ T][pos[tmp]]) , tmp -= (1 << (pos[tmp] - 1)) ;
		}
		for(int T = S; T ; T = (T - 1) & S) 
			if(S ^ T) add(g[S] , mod - 1ll * g[T] * pw[Inside[S ^ T]] % mod * pw[num[T]] % mod) ;
	}
	for(int S = 1; S < mx; S ++) {
		for(int s1 = S; s1 ; s1 = (s1 - 1) & S) {
			if(s1 & (1 << (pos[S] - 1))) add(f[S] , 1ll * f[s1] * g[S ^ s1] % mod) ; 
		}
		add(f[S] , g[S]) ; 
	}
	cout << f[mx - 1] << endl ; return 0 ; 
}

posted @ 2018-12-02 22:10  GuessYCB  阅读(748)  评论(0编辑  收藏  举报