【bzoj3812】【清华集训2014】主旋律(容斥,计数)

毒 瘤 计 数!

XSY 题意不是很清楚,这里给出更加清楚的:

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

\(E[U,V]\) 表示题目给的所有边中,起点在 \(U\) 中、终点在 \(V\) 中的所有边的集合,其中 \(U,V\) 为点集。

对于一个点集 \(S\),设其任意形成的某一张图为 \(G\)(“形成”是指通过题目给的边形成),我们对其连边后缩点,最后得到的肯定是若干个强连通分量(称为 scc)之间有连边,形成一个 DAG。而题目要求的是只有一个 scc,那么我们就要用 \(S\) 能形成的图的总数量 \(2^{E[S,S]}\) 减去 \(S\) 形成的图是多个 scc 的 DAG 的情况。

那么我们枚举这个 DAG 中,出度为 \(0\) 的那些 scc 所包含的点的集合 \(T\),然后再考虑在确定了 \(T\) 的基础上再确定 \(G\) 的数量。

首先 \(S-T\)(中间是减号)的点之间可以乱连,\(S-T\) 的点向 \(T\) 的点也可以乱连,这部分的方案是 \(2^{E[S-T,S-T]}\times 2^{E[S-T,T]}\)

于是我们可以暂时建立一个等量关系:

\[2^{E[S,S]}=\sum_{T\subseteq S,T\neq \emptyset}g_T\times 2^{E[S-T,S-T]}\times 2^{E[S-T,T]} \]

其中 \(g_T\) 可以暂时理解为点集 \(T\) 形成的图为若干个强联通分量的方案数,但显然这样定义是不对的,原因下面会说。

\(f_X\) 表示点集 \(X\) 形成的图为一个强联通分量的方案数。(那么 \(f\) 就是题目所求)

那么就可以直接 \(g_T=\sum\limits_{s_1,s_2,\cdots,s_k}\prod\limits_{i=1}^k f_{s_i}\) 了吗?(其中 \(\sum\limits_{s_1,s_2,\cdots,s_k}\) 的意思是将 \(T\) 划分为若干个scc \(s_1,s_2,\cdots,s_k\),即枚举 \(s_1\cup s_2\cup\cdots\cup s_k=T\) 满足 \(\forall i\neq j,s_i\cap s_j=\emptyset\)

当然不行,因为 \(E[S-T,S-T]\) 里面的是随便连的,有可能会形成其他的出度为 \(0\) 的 scc,这与我们的假设不符,会导致算重。这也就是 \(g_T\) 不能直接定义为“点集 \(T\) 形成的图为若干个强联通分量的方案数”的原因。

因此我们需要容斥。于是设容斥系数 \(coef_k\)

\[g_T=\sum\limits_{s_1,s_2,\cdots,s_k}coef_k\prod\limits_{i=1}^k f_{s_i} \]

对于 \(T\) 的某一种划分方案 \(s_1,s_2,\cdots,s_k\),我们在 \(s_1,s_2,\cdots,s_k\) 中任意选出若干个并起来得到 \(T'\),那么当我们在枚举 \(T=T'\) 时,这种方案都被算了一次,于是这种方案实际上被算了:

\[\sum_{i=0}^k\binom{k}{i}coef_i \]

理论上,这种方案应该只能被算一次,于是:

\[1=\sum_{i=1}^k\binom{k}{i}coef_i \]

根据二项式反演:

\[\begin{aligned} coef_k&=\sum_{i=1}^k(-1)^{k-i}\binom{k}{i}\\ &=\left(\sum_{i=0}^k\binom{k}{i}1^i(-1)^{k-i}\right)-(-1)^k\\ &=(1-1)^k-(-1)^k\\ &=(-1)^{k+1} \end{aligned} \]

于是:

\[g_T=\sum\limits_{s_1,s_2,\cdots,s_k}(-1)^{k+1}\prod\limits_{i=1}^k f_{s_i} \]

如果求出了 \(g\),那么就能跟 \(f\) 扯上点关系。

根据我们刚开始那条式子可以对 \(g\) 进行递推:

\[\begin{aligned} 2^{E[S,S]}&=\sum_{T\subseteq S,T\neq \emptyset}g_T\times 2^{E[S-T,S-T]}\times 2^{E[S-T,T]}\\\\ g_S&=2^{E[S,S]}-\sum_{T\subsetneqq S,T\neq \emptyset}g_T\times 2^{E[S-T,S-T]}\times 2^{E[S-T,T]} \end{aligned} \]

于是 \(g\) 求出来了。

观察刚刚推出来的 \(g\)\(f\) 的式子:

\[g_T=\sum\limits_{s_1,s_2,\cdots,s_k}(-1)^{k+1}\prod\limits_{i=1}^k f_{s_i} \]

那么:

\[\begin{aligned} g_T=(-1)\times \sum_{s_1\subseteq T,s_1\neq \emptyset}f_{s_1}g_{T-s_1}\\ \end{aligned} \]

(其中 \(s_1\) 枚举的是 \(T\) 中编号最小的点所在的scc)

也由此可知应该令 \(g_{\emptyset}=-1\)

于是:

\[\begin{aligned} g_T=f_T+(-1)\times\sum_{s_1\subsetneqq T,s_1\neq \emptyset}f_{s_1}g_{T-s_1}\\ f_T=g_T+\sum_{s_1\subsetneqq T,s_1\neq \emptyset}f_{s_1}g_{T-s_1} \end{aligned} \]

于是就能求出 \(f_S\) 了。

这道题的总体思路是先求 \(S\) 形成的图是若干个出度为 \(0\) 的 scc 的方案数 \(g_S\)(用总的方案数减去 DAG 的方案数),再求 \(S\) 形成的图是一个出度为 \(0\) 的 scc 的方案数 \(f_S\)(用 \(g_S\) 减去多个 scc 的方案数)。

#include<bits/stdc++.h>

#define N 17
#define PN 33000

using namespace std;

namespace modular
{
	const int mod=1000000007;
	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
	inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
	inline int mul(int x,int y){return 1ll*x*y%mod;}
}using namespace modular;

inline int poww(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1) ans=mul(ans,a);
		a=mul(a,a);
		b>>=1;
	}
	return ans;
}

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int n,m,maxn,E1[PN],E2[PN][N];
int pow2[N*N],minid[PN];
int f[PN],g[PN];

void init()
{
	pow2[0]=1;
	for(int i=1;i<=m;i++)
		pow2[i]=add(pow2[i-1],pow2[i-1]);
	maxn=pow2[n]-1;
	for(int i=1;i<=maxn;i++)
	{
		if(i&1) minid[i]=1;
		else minid[i]=minid[i>>1]+1;
	}
}

int main()
{
	n=read(),m=read();
	if(m<n)
	{
		puts("0");
		return 0;
	}
	init();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		int pu=pow2[u-1],pv=pow2[v-1];
		for(int S=maxn^pu^pv,now=S;;now=(now-1)&S)
		{
			E1[now^pu^pv]++;
			if(!now) break;
		}
		for(int S=maxn^pu,now=S;;now=(now-1)&S)
		{
			if((now>>(v-1))&1) continue;
			E2[now^pu][v]++;
			if(!now) break;
		}
	}
	g[0]=mod-1;
	for(int S=1;S<=maxn;S++)
	{
		g[S]=pow2[E1[S]];
		for(int T=(S-1)&S;T!=0;T=(T-1)&S)
		{
			int sum=0,now=T;
			while(now)
			{
				sum+=E2[S^T][minid[now]];
				now^=pow2[minid[now]-1];
			}
			g[S]=dec(g[S],mul(g[T],mul(pow2[E1[S^T]],pow2[sum])));
		}
	}
	for(int T=1;T<=maxn;T++)
	{
		f[T]=g[T];
		int p=pow2[minid[T]-1],S=T^p;
		if(!S) continue;
		for(int s1=(S-1)&S;;s1=(s1-1)&S)
		{
			f[T]=add(f[T],mul(f[s1^p],g[T^(s1^p)]));
			if(!s1) break;
		}
	}
	printf("%d\n",f[maxn]);
	return 0;
}
posted @ 2022-10-28 19:40  ez_lcw  阅读(159)  评论(0编辑  收藏  举报