DennyQi's Log

02 容斥原理

容斥原理

集合的并集

让我们从一个简单的计数问题作为例子出发。在11100100的整数中,我们想数出“是223355的数的倍数”的数的个数,怎么数比较方便呢?我们很容易数出22的倍数的个数、33的倍数的个数、55的倍数的个数,它们分别是100/2=50100/2=50100/3=33\left\lfloor 100/3\right\rfloor=33100/5=20100/5=20。但是,不能把它们简单地加起来,因为有的数又是22的倍数又是33的倍数,有的数又是33的倍数又是55的倍数,有的数同时是2,3,52,3,5的倍数。所以,我们要把重复的给减掉。同时是2233的倍数的数的个数为100/(2×3)=16\left\lfloor 100/(2\times 3)\right\rfloor=16,同时是2255的倍数的的数的个数为100/(2×5)=10\left\lfloor 100/(2\times 5)\right\rfloor=10,同时是3355的倍数的的数的个数为100/(3×5)=6\left\lfloor 100/(3\times 5)\right\rfloor=6。但是,又不能全都减掉,因为因为同时是2,3,52,3,5的倍数的数之前被多加了两次,现在又被减了三次。所以我们最后还需要加上一次100/(2×3×5)=3\left\lfloor 100/(2\times3\times 5)\right\rfloor=3。综上所述,在11100100的整数中“是223355的数的倍数”的数的个数为:50+33+2016106+3=7450+33+20-16-10-6+3=74

上述计数过程的关键在于弄清同时满足多个性质的元素集合之间的交集关系。一个更清晰的办法是用韦恩图表示:设11100100之间22的倍数的集合为AA33的倍数的集合为BB55的倍数的集合为CC,求集合ABC|A\cup B\cup C|的大小。在这个问题里,很关键的一点是:我们很容易计算A,B,C|A|,|B|,|C|AB|A\cap B|AC|A\cap C|BC|B \cap C|ABC|A\cap B\cap C|这些集合的大小,所以我们希望建立这些集合与ABCA\cup B\cup C之间的计数关系。从例子中,我们找到了这样一个恒成立的关系:

ABC=A+B+CABACBC+ABC|A\cup B\cup C|=|A|+|B|+|C|-|A\cap B|-|A\cap C|-|B\cap C|+|A\cap B\cap C|

上面这个关系可以推广到任意nn个集合上:对于有限大小的集合A1,,AnA_1,\cdots,A_n,设A={A1,,An}\mathscr{A}=\{A_1,\cdots,A_n\},那么:

i[n]Ai=JA(1)J+1AiJAi\left|\bigcup\limits_{i\in [n]}A_i\right|=\sum\limits_{J\subseteq \mathscr{A}}(-1)^{|J|+1}\left|\bigcap\limits_{A_i\in J} A_i\right|

这称为容斥原理(inclusion-exclusion principle)。

“坏性质”

在组合数学中,通常会用到另一种方法来叙述容斥原理。这种方法和上面的“求集合的并集”的形式相比,在本质上是完全相同的。我们下面用新的方法重新叙述容斥原理,并给出证明。

我们可以这样看待容斥原理:在全集UU中,有一系列性质是“坏性质”,我们要求出“好元素”——不满足任何坏性质的元素——的个数。注意,一个“性质”就是一个UU的子集,这是一种自然语言习惯。通常而言,如果同时满足多个坏性质的元素个数是容易计算的,那么我们就可以用容斥原理求好元素。比如,在上一段的例子中,我们就把“22的倍数”、“33的倍数”、“55的倍数”分别看作三个坏性质,那么只要求出好元素的个数(既不是22的倍数也不是33的倍数也不是55的倍数),再取补集就得到了答案(223355的倍数)。用记号表示:在给定的一个大集合UU中,设坏集合为A1,,AnA_1,\cdots,A_n。已知i=1kApi|\bigcap\limits_{i=1}^{k}A_{p_i}|是容易计算的,目标就是计算出i=1nAi|\bigcup\limits_{i=1}^{n} A_i|。令A={A1,,Am}\mathscr{A}=\{A_1,\cdots,A_m\}。对任意JAJ\subseteq A,定义:

N=(J):={xUAiJ,xAiAiAJ,xAi}N_=(J):=\left|\{x \in U \mid \forall A_i \in J, x \in A_i 且 \forall A_i \in \mathscr{A} \setminus J, x \notin A_i\}\right|

N=(J)N_=(J)表示所有满足JJ中的所有坏性质,而不满足任何其它坏性质的元素个数。比如在刚才的例子中,把三个坏性质“22的倍数”、“33的倍数”、“55的倍数”分别记为A1,A2,A3A_1,A_2,A_3,那么N=({A1,A3})N_=(\{A_1,A_3\})就表示“既是22的倍数又是55的倍数,但不是33的倍数”的数的个数(也就是(A1A3)A2|(A_1\cap A_3)\setminus A_2|)。注意到,N=()N_=(\varnothing)就表示不满足任何坏性质的元素个数,也就是“好元素”个数。对任意JAJ\subseteq A,定义:

N(J)={xUAiJ,xAi}N_\geq (J)=|\{x \in U \mid \forall A_i \in J, x \in A_i\}|

N=(J)N_=(J)表示所有满足JJ中的所有坏性质的元素个数,它其实就相当于AiJAi\bigcap\limits_{A_i\in J}A_i的大小,只不过J=J=\varnothingN()N_\geq (\varnothing)表示全集而不是空集。在可以使用容斥原理的问题中,N()N_\geq(\varnothing)通常是容易求出的。

N=N_=NN_\geq的定义”也完全可以用“坏性质集合间的交集、并集”来描述,但是要注意边界条件:“满足0个坏性质”对应的是全集,而不是空集。

在这样的记号下,容斥原理的表述为:

N=()=JA(1)JN(J)N_=(\varnothing) = \sum\limits_{J\subseteq \mathscr{A}} (-1)^{|J|}N_\ge(J)

计算时可以写为:(其中(Aj)\dbinom{\mathscr{A}}{j}表示全体大小为jjA\mathscr{A}的子集)

N=()=j=0m(1)jJ(Aj)N(J)N_=(\varnothing) = \sum\limits_{j=0}^m (-1)^j \sum\limits_{J\in\binom{\mathscr{A}}{j}} N_\ge(J)

证明:我们考虑每个元素的“贡献”(“贡献”是计数中的一种证明方法,上面的等式的左右两边都是在数数,要证明等式相等,只需证明任意xUx\in U被等式左边数到的次数和被等式右边数到的次数是相等的)。如果xN=()x \in N_=(\varnothing),将在左侧贡献1。这意味着xx不属于任何坏集合,因此它在右侧只会在j=0j=0时(即J=J=\varnothing)时被统计到,刚好在N()N_\geq(\varnothing)中贡献1次;如果xN=()x \notin N_=(\varnothing),那么左侧贡献00,此时xx可能出现在了某几个坏集合中,不妨设出现在Ai1,,AitA_{i_1},\cdots,A_{i_t}中,那么当且仅当J{Ai1,,Ait}J \subseteq \{A_{i_1},\cdots,A_{i_t}\}时(包括空集)右侧才会贡献,总的贡献恰好为j=0t(1)j(tj)\sum\limits_{j=0}^{t}(-1)^j \dbinom{t}{j},由二项式定理,这恰好等于j=0t(tj)(1)j(1)tj=(1+1)t=0\sum\limits_{j=0}^{t}\dbinom{t}{j}(-1)^j (1)^{t-j}=(-1+1)^t=0。证毕。

我们可以证明更一般的容斥原理。对于任意SAS\subseteq \mathscr{A}

N=(S)=J:SJA(1)JSN(J)N_=(S)=\sum\limits_{J:S \subseteq J \subseteq \mathscr{A}}(-1)^{|J|-|S|}N_\geq(J)

证明:考虑每个元素的贡献。xN=(S)\forall x \in N_=(S),左边贡献1;等式右边当且仅当xN(J)x \in N _\geq (J)才产生贡献,这要求JSJ \subseteq S。而SJS \subseteq J,因此必须有S=JS=J。因此等式右边也恰好贡献1。如果xN=(S)x \notin N_=(S),则左边贡献0;等式右边,如果本身就有S=AS=\mathscr{A},则右侧等式直接可以写作N(A)=N=(A)N _\geq(\mathscr{A})=N_=(\mathscr{A}),等式成立。如果SAS \subsetneq \mathscr{A},此时分类讨论:①AkS,x∉Ak\exists A_k\in S,x\not\in A_k,那么因为SJS\subseteq J,所以对于所有等式中的JJN(J)N_\geq(J)的贡献都为00,成立。②AkS,xAk\forall A_k\in S,x\in A_k同时AkJS,xAk\exists A_k\in J\setminus S,x\notin A_k。此时,记Tx={AiAxAi}T_x=\{A_i\in \mathscr{A}\mid x\in A_i\},那么当且仅当JTxJ\subseteq T_xN(J)N_\geq (J)才产生贡献。所以,等式右侧的贡献为SJTx(1)JSN(J)=j=STx(1)jSSJ(Txj)N(J)\sum\limits_{S \subseteq J \subseteq T_x}(-1)^{|J|-|S|}N_\geq(J)=\sum\limits_{j=|S|}^{|T_x|}(-1)^{j-|S|} \sum\limits_{S \subseteq J \in \binom{T_x}{j}} N_\geq(J) =j=0TxS(1)j(TxSj)=(1+1)TxS=0=\sum\limits_{j=0}^{|T_x|-|S|}(-1)^j \dbinom{|T_x|-|S|}{j}=(-1+1)^{|T_x|-|S|}=0。证毕。

容斥原理的应用

错位排列

一个排列{ai}i=1n\{a_i\}_{i=1}^{n}是错位排列当且仅当i[n],aii\forall i\in [n],a_i \neq i。求长度为nn的错位排列的个数fnf_n

设全集为UU,共包含n!n!个排列。对任意i[n]i\in [n],把所有满足ai=ia_i=i的排列集合看作坏性质AiA_i。那么,容易发现N({Ai1,,Ait})=(nt)!N_\geq(\{A_{i_1},\cdots,A_{i_t}\})=(n-t)!

代入容斥原理的公式,N=()=j=0n(1)jJ(Aj)(nj)!=j=0n(1)j(nj)(nj)!N_=(\varnothing)=\sum\limits_{j=0}^n (-1)^j \sum\limits_{J\in\binom{\mathscr{A}}{j}} (n-j)!=\sum\limits_{j=0}^n (-1)^j \dbinom{n}{j} (n-j)!。所以

fn=j=0n(1)j(nj)(nj)!f_n=\sum\limits_{j=0}^n (-1)^j \dbinom{n}{j} (n-j)!

这就是错位排列的通项公式。

第二类斯特林数通项

kk个球放进nn个箱子,球不同,箱子相同,箱子不能为空”的答案是第二类斯特林数{kn}\left\{\begin{array}{l} k \\ n \end{array}\right\}。我们还证明过,“kk个球放进nn个箱子,球不同,箱子不同,箱子不能为空”的答案是{kn}n!\left\{\begin{array}{l} k \\ n \end{array}\right\}n!,因为只需在箱子相同的答案的基础上对箱子做轮换即可。现在我们利用容斥原理来计算后面这个小球装箱问题,由此给出第二类斯特林数的通项公式。

kk个球放进nn个箱子,球不同,箱子不同,箱子不能为空”可以看作:求“从[k][k][n][n]的满射”的个数。从[k][k][n][n]的映射共有nkn^k个,把这看作全集UU。对于任意i[n]i\in [n],把“ii没有被映射到的映射”看作坏性质集合AiA_i,那么有N({Ai1,,Ait})N_\geq(\{A_{i_1},\cdots,A_{i_t}\}) =(nt)k=(n-t)^k。于是有N=()=j=0n(1)jJ(Aj)(nj)k=j=0n(1)j(nj)(nj)kN_=(\varnothing)=\sum\limits_{j=0}^n (-1)^j \sum\limits_{J\in\binom{\mathscr{A}}{j}} (n-j)^k=\sum\limits_{j=0}^n (-1)^j \dbinom{n}{j} (n-j)^k

综上,我们得到:

{kn}=1n!j=0n(1)j(nj)(nj)k\left\{\begin{array}{l} k \\ n \end{array}\right\}=\dfrac{1}{n!}\sum\limits_{j=0}^n (-1)^j \dbinom{n}{j} (n-j)^k

二分图完美匹配

给定2n2n个顶点的二分图,二分图的两侧各nn个节点。设Ai1,i2A_{i_1,i_2}表示左侧的第i1i_1个点与右侧的第i2i_2个点直接是否有边相连(有则为11,无则为00)。一个“完美匹配(perfect match)”是nn条边,分别连接左右的全部点。给定邻接矩阵,我们可以枚举nn上的全排列σ\sigma,计算该二分图上的完美匹配方案数:σi=1nAi,σ(i)\sum\limits_{\sigma}\prod\limits_{i=1}^{n}A_{i,\sigma(i)}。这样做的计算复杂度为O(n!n)O(n! \cdot n)

我们可以利用容斥原理计算完美匹配方案数。把n!n!种全排列看作全集UU。把“右侧的节点ii没有被匹配上的匹配”看作坏性质集合AiA_i,那么N(J)=i=1n(j[n]\JAi,j)N_\geq(J)=\prod\limits_{i=1}^{n}\left(\sum\limits_{j \in [n] \backslash J}A_{i,j}\right)。于是有N=()=JA(1)Ji=1n(j[n]\JAi,j)N_=(\varnothing)=\sum\limits_{J\in\mathscr{A}}(-1)^{|J|} \prod\limits_{i=1}^{n}\left(\sum\limits_{j \in [n] \backslash J}A_{i,j}\right)。用这个式子计算,不再需要枚举全排列,只需要枚举大小为nn的集合的子集。如果对后面部分做好预处理,复杂度可以达到O(2nn)O(2^n \cdot n)。可以证明,在渐进意义下这是最优复杂度了。

参考资料

[1] Chihao Zhang, Combinatorics in Computer Science (Spring 2023)