容斥原理及证明


定理

设共有\(n\)个集合,\(A_i\)表示第\(i\)个集合,则所有集合的并集可表示成以下形式:

\[|A_1\cup A_2\cup \cdots\cup A_n|=\sum_{i=1}^n (-1)^{i-1}\sum|A_1\cap A_2\cap\cdots\cap A_i| \]

证明

设某个元素被\(x\)个集合包含,显然地,其对左式的贡献为1,因为在并集中只计算一次。
考虑其对于右式的贡献,它会在这\(x\)个集合的所有子集中被计算到。其贡献为:

\[\sum_{i=1}^x C_x^i(-1)^{i-1}=-\sum_{i=1}^x C_x^i(-1)^{i}=1-\sum_{i=0}^x C_x^i(-1)^{i}=1-\sum_{i=0}^x C_x^i(-1)^{i}\times1^{x-i}=1-(1-1)^x=1 \]

由于所有元素对于左右两式的贡献均为1,综上即可证得等式成立。

推论

\(A_i^c\)表示\(A_i\)的补集,\(S\)表示全集,则:

\[|A_1^c\cap A_2^c\cap\cdots\cap A_n^c|=|S|-|A_1\cup A_2\cup \cdots\cup A_n|=|S|-\sum_{i=1}^n (-1)^{i-1}\sum|A_1\cap A_2\cap\cdots\cap A_i| \]

常用于解决限制条件较繁复的问题。

相关练习

[JSOI2015]染色问题

题解

[click]

这三个限制可以逐个来解决,然后逐层相乘。
当至少有\(i\)\(j\)列不染色的规定为某\(i\)\(j\)列不染色的时候,设\(f_k\)表示不用\(k\)种颜色的方案。
则:

\[\binom{c}{k}(c-k+1)^{(n-i)(m-j)}=\sum_{t=k}^c \binom{t}{k} f_t \]

左式的意义为,选\(k\)个颜色不用的方案数。由于还有不涂色的选择,所以并不能保证只有那几个没有用到。假设有一种方案恰好有\(t\)种颜色没用到,根据这样的选择方式,它就被计算了\(\binom{t}{k}\)次。由此得出上式。
二项式反演一下,或者从单纯的容斥角度而言,即可得到:

\[f_k=\sum_{t=k}^c (-1)^{t-k}\binom{t}{k}\binom{c}{t}(c-t+1)^{(n-i)(m-j)} \]

\[\begin{align*} \sum_{k=1}^c f_k& =\sum_{k=1}^c \sum_{t=k}^c (-1)^{t-k}\binom{t}{k}\binom{c}{t}(c-t+1)^{(n-i)(m-j)}\\ & =\sum_{t=1}^c \binom{t}{k}\binom{c}{t}(c-t+1)^{(n-i)(m-j)} \sum_{k=1}^t (-1)^{t-k}\binom{t}{k}\\ & =\sum_{t=1}^c \binom{t}{k}\binom{c}{t}(c-t+1)^{(n-i)(m-j)} (-(-1)^t +\sum_{k=0}^t (-1)^{t-k}1^k \binom{t}{k})\\ & =\sum_{t=1}^c (-1)^{t+1}\binom{c}{t}(c-t+1)^{(n-i)(m-j)}\\ \end{align*}\]

合法的方案数即为:

\[(c+1)^{(n-i)(m-j)}-\sum_{k=1}^c f_k=\sum_{k=0}^c (-1)^k\binom{c}{k} (c-k+1)^{(n-i)(m-j)} \]

这样的方案数,还是会算重,同样因为有不涂色的选择会导致超过\(i\)行或\(j\)列是完全空白的。再进行容斥,即可得到:

\[\begin{align*} & \sum_{i=0}^n (-1)^i \binom{n}{i}\sum_{j=0}^m (-1)^j \binom{m}{j} \sum_{k=0}^c (-1)^k\binom{c}{k}(c-k+1)^{(n-i)(m-j)}\\ & =\sum_{i=0}^n\sum_{k=0}^c (-1)^{i+k}\binom{n}{i} \binom{c}{k}\sum_{j=0}^m \binom{m}{j}(-1)^j(c-k+1)^{(n-i)(m-j)}\\ & =\sum_{i=0}^n\sum_{k=0}^c (-1)^{i+k}\binom{n}{i} \binom{c}{k}\sum_{j=0}^m [-1+(c-k+1)^{n-i}]^m \\ \end{align*}\]

如果不将后面的求和转化掉的话,复杂度是\(O(n^3)\)。转化后对每一个\(c-k+1\)的幂进行预处理,即可达到\(O(n^2logn)\)的复杂度。

代码

[click]
#include 
#include 
typedef long long ll;
const int p=1e9+7;
const int maxn=400+10;
int fac[maxn],inv[maxn],pow[maxn*maxn];

int max(int x,int y) {return x>y?x:y;}
int read()
{
	int res=0;
	char ch=getchar();
	while(!isdigit(ch))
		ch=getchar();
	while(isdigit(ch))
		res=res*10+ch-'0',ch=getchar();
	return res;
}
int power(int a,int n)
{
	int res=1;
	while(n)
	{
		if (n&1)
			res=(ll)res*a%p;
		a=(ll)a*a%p;
		n>>=1;
	}
	return res;
}
void prework(int n)
{
	fac[0]=1;
	for (int i=1;i<=n;i++)
		fac[i]=(ll)fac[i-1]*i%p;
	inv[n]=power(fac[n], p-2);
	for (int i=n-1;i>=0;i--)
		inv[i]=(ll)inv[i+1]*(i+1)%p;
}
int C(int n,int m)
{
	if (m>n)
		return 0;
	return (ll)fac[n]*inv[m]%p*inv[n-m]%p;
}
int main()
{
	int n=read(),m=read(),c=read();
	prework(max(max(n, m), c));
	int ans=0;
	pow[0]=1;
	for (int k=0;k<=c;k++)
	{
		for (int i=1;i<=n;i++)
			pow[i]=(ll)pow[i-1]*(c-k+1)%p;
		for (int i=0;i<=n;i++)
		{
			int mul=(ll)power(pow[n-i]-1, m)*C(n, i)%p*C(c, k)%p;
			if ((i^k)&1)
				ans-=mul;
			else
				ans+=mul;
			if (ans<0)
				ans+=p;
			else if (ans>=p)
				ans-=p;
		}
	}
	printf("%d\n",ans); 
	return 0;
}