容斥原理

容斥的原理,代码实现,常见应用。

容斥原理及其应用

容斥原理

有一个集合SS,有P1,P2,...PmP_1,P_2,...P_mmm个性质,SS中具有性质PiP_i的元素构成集合AiA_i,则:

A1ˉA2ˉ...Amˉ=SAi+AiAjAiAjAk+...+(1)mA1...Am|\bar{A_1}\bigcap\bar{A_2}\bigcap...\bar{A_m} | = |S| - \sum|A_i|+\sum|A_i\bigcap A_j|-\sum|A_i\bigcap A_j\bigcap A_k| + ... + (-1)^m|A_1\bigcap...A_m|

这就是容斥原理。

容斥原理的证明思路是:

  • 对任意xA1ˉA2ˉ...Amˉx\in \bar{A_1}\bigcap\bar{A_2}\bigcap...\bar{A_m},证明xx对等式左右两边的贡献都是11
  • 对任意xA1ˉA2ˉ...Amˉx\notin \bar{A_1}\bigcap\bar{A_2}\bigcap...\bar{A_m}xSx\in S,证明xx对等式左右两边的贡献都是00

由容斥原理很容易得到一个推论:

集合SS中至少具有性质P1,P2,...PmP_1,P_2,...P_m之一的对象的个数为:

A1A2...Am=AiAiAj+...+(1)m1A1A2...Am|A_1\bigcup A_2\bigcup...A_m | = \sum |A_i| - \sum|A_i \bigcap A_j| +... +(-1)^{m - 1}|A_1\bigcap A_2 \bigcap... A_m|

式子可以由德摩根律轻松推出。

使用动机

容斥原理的使用经常出于以下两种动机:

  • 从反面思考问题容易思考计数的策略。

  • 从反面方便批量进行计数,比正面直接计算时间复杂度低。常见的情形是计数时发现计数结果只和选择的集合个数有关,与到底选了哪几个集合无关。这个时候有:

    A1ˉA2ˉ...Amˉ=i=0m(1)i(mi)αi|\bar{A_1}\bigcap\bar{A_2}\bigcap...\bar{A_m} | = \sum_{i = 0}^m(-1)^i\tbinom{m}{i}\alpha_i

    其中,αi\alpha_i代表选择ii个集合的时候产生的贡献。容易发现这个式子的计算是O(n)O(n)的。而一般的容斥在计算的时候由于需要枚举子集,所以计算量至少是O(2n)O(2 ^n)的。

应用举例

下面介绍一些容斥原理的应用,既包含比较偏组合数学课程的应用,也包含偏算法竞赛的应用。

普通集合上的应用

  • 1110001000中不能被5,65,688整除的数的个数:

    利用除法分别算出来能被5,6,85,6,8整除的数的个数,然后计算能被两两组合的最小公倍数整除的数的个数,最后计算能被三个数的最小公倍数整除的数的个数,容斥一下即可得到答案。

  • 009999999999中有多少同时含有2,5,82,5,8的整数:

    可以按照上一题的思路统计,也可以注意到不含有11个数的时候,不管是不含有2,5,82,5,8中的哪一个,贡献都一样多,所以可以较为简单的计数。

  • 确定S={1,2,,8}S=\{1, 2,…, 8\}的排列中没有偶数在它的自然位置上的排列数:

    同样可以发现可以按照有1,2,3,41,2,3,4个偶数在其自然位置上进行计数,然后容斥计算结果。

  • 确定S={1,2,,8}S=\{1, 2,…, 8\}的排列中至少有一个奇数在它的自然位置上的排列数:

    可以考虑四个性质分别是1,3,5,71,3,5,7在其自然位置上,则最终计数的就是A1A2...A4|A_1\bigcup A_2...\bigcup A_4|,容斥原理推论即可。

多重集上的应用

基础知识:kk种元素,每种元素可以取无限个,从这kk种元素中取恰好nn个元素,求方案数可以使用隔板法,结果是(n+k1k1)\tbinom{n + k - 1}{k - 1}

利用容斥原理进行拓展:

  • 确定多重集T={3.a,4.b,5.c}T=\{3.a, 4.b, 5.c\}1010子集的个数:

    可以看到并非每种元素可以随便选择。但是,我们仍然可以基于任意选择的方法利用容斥进行计数。考虑集合S={a,b,c}S = \{\infin a, \infin b, \infin c\}A1A_1表示从SS中拿出的包含不少于44aa的集合的集合,A2A_2表示从SS中拿出的包含不少于55bb的集合的集合,A3A_3表示从SS中拿出的包含不少于66cc的集合的集合。若对SS做容斥,去掉这些个情况的方案,那么得到的就是TT中取1010子集的方案数,即A1ˉA2ˉA3ˉ|\bar{A_1}\bigcap \bar{A_2}\bigcap \bar{A_3}|

  • 不定方程整数解的个数,每个变量有一定的范围限制:

    做变量变换,把所有变量都调整成xi0x_i \geq 0,然后就转化成了上一个问题。

错位排列

枚举有多少个数在它原来的位置,使用容斥原理,有:

Dn=n!(n1)(n1)!+(n2)(n2)!(n3)(n3)!+...+(1)n(nn)0!D_n = n! - \tbinom{n}{1}(n - 1)! + \tbinom{n}{2}(n - 2)! - \tbinom{n}{3}(n - 3)! + ... + (-1)^n\tbinom{n}{n}0!\\

把组合数算出来,化简一下:

Dn=n!(111!+12!+...+(1)n1n!)D_n = n!(1 - \frac{1}{1!} + \frac{1}{2!} + ... + (-1)^n\frac{1}{n!})

这就是错位排列数的计算公式了。

也可以通过递推的方式计算错位排列数DnD_n

  • 把数11放到其他位置上,然后把该位置原来的数放到11原来的位置上(即把11原来的位置当作被占用的那个位置原来的数的新的“原位”),有(n1)Dn2(n - 1)D_{n - 2}种方案。
  • 把数11放到其他位置上,然后不把该位置原来的数放到11原来的位置上,有(n1)Dn1(n - 1)D_{n - 1}种方案。

所以,Dn=(n1)(Dn1+Dn2)D_n = (n - 1)(D_{n - 1} + D_{n - 2})D1=0,D2=1D_1 = 0, D_2 = 1

带有禁止位置的排列

没有什么很套路的计算方法,甚至可能使用容斥原理会使计算更加复杂。

带有绝对禁止位置的排列计数问题,在集合之间没有明显规律的时候,使用容斥原理统计可能会比较复杂,使用程序实现容斥原理也很可能是指数级的计算复杂度。

带有

AiA_i:第i+1i + 1个人前面的人是上一次的人

几何问题