容斥的原理,代码实现,常见应用。
容斥原理及其应用
容斥原理
有一个集合S,有P1,P2,...Pm共m个性质,S中具有性质Pi的元素构成集合Ai,则:
∣A1ˉ⋂A2ˉ⋂...Amˉ∣=∣S∣−∑∣Ai∣+∑∣Ai⋂Aj∣−∑∣Ai⋂Aj⋂Ak∣+...+(−1)m∣A1⋂...Am∣
这就是容斥原理。
容斥原理的证明思路是:
- 对任意x∈A1ˉ⋂A2ˉ⋂...Amˉ,证明x对等式左右两边的贡献都是1
- 对任意x∈/A1ˉ⋂A2ˉ⋂...Amˉ且x∈S,证明x对等式左右两边的贡献都是0
由容斥原理很容易得到一个推论:
集合S中至少具有性质P1,P2,...Pm之一的对象的个数为:
∣A1⋃A2⋃...Am∣=∑∣Ai∣−∑∣Ai⋂Aj∣+...+(−1)m−1∣A1⋂A2⋂...Am∣
式子可以由德摩根律轻松推出。
使用动机
容斥原理的使用经常出于以下两种动机:
-
从反面思考问题容易思考计数的策略。
-
从反面方便批量进行计数,比正面直接计算时间复杂度低。常见的情形是计数时发现计数结果只和选择的集合个数有关,与到底选了哪几个集合无关。这个时候有:
∣A1ˉ⋂A2ˉ⋂...Amˉ∣=i=0∑m(−1)i(im)αi
其中,αi代表选择i个集合的时候产生的贡献。容易发现这个式子的计算是O(n)的。而一般的容斥在计算的时候由于需要枚举子集,所以计算量至少是O(2n)的。
应用举例
下面介绍一些容斥原理的应用,既包含比较偏组合数学课程的应用,也包含偏算法竞赛的应用。
普通集合上的应用
-
求1到1000中不能被5,6或8整除的数的个数:
利用除法分别算出来能被5,6,8整除的数的个数,然后计算能被两两组合的最小公倍数整除的数的个数,最后计算能被三个数的最小公倍数整除的数的个数,容斥一下即可得到答案。
-
求0到99999中有多少同时含有2,5,8的整数:
可以按照上一题的思路统计,也可以注意到不含有1个数的时候,不管是不含有2,5,8中的哪一个,贡献都一样多,所以可以较为简单的计数。
-
确定S={1,2,…,8}的排列中没有偶数在它的自然位置上的排列数:
同样可以发现可以按照有1,2,3,4个偶数在其自然位置上进行计数,然后容斥计算结果。
-
确定S={1,2,…,8}的排列中至少有一个奇数在它的自然位置上的排列数:
可以考虑四个性质分别是1,3,5,7在其自然位置上,则最终计数的就是∣A1⋃A2...⋃A4∣,容斥原理推论即可。
多重集上的应用
基础知识:k种元素,每种元素可以取无限个,从这k种元素中取恰好n个元素,求方案数可以使用隔板法,结果是(k−1n+k−1) 。
利用容斥原理进行拓展:
-
确定多重集T={3.a,4.b,5.c}的10子集的个数:
可以看到并非每种元素可以随便选择。但是,我们仍然可以基于任意选择的方法利用容斥进行计数。考虑集合S={∞a,∞b,∞c},A1表示从S中拿出的包含不少于4个a的集合的集合,A2表示从S中拿出的包含不少于5个b的集合的集合,A3表示从S中拿出的包含不少于6个c的集合的集合。若对S做容斥,去掉这些个情况的方案,那么得到的就是T中取10子集的方案数,即∣A1ˉ⋂A2ˉ⋂A3ˉ∣ 。
-
不定方程整数解的个数,每个变量有一定的范围限制:
做变量变换,把所有变量都调整成xi≥0,然后就转化成了上一个问题。
错位排列
枚举有多少个数在它原来的位置,使用容斥原理,有:
Dn=n!−(1n)(n−1)!+(2n)(n−2)!−(3n)(n−3)!+...+(−1)n(nn)0!
把组合数算出来,化简一下:
Dn=n!(1−1!1+2!1+...+(−1)nn!1)
这就是错位排列数的计算公式了。
也可以通过递推的方式计算错位排列数Dn:
- 把数1放到其他位置上,然后把该位置原来的数放到1原来的位置上(即把1原来的位置当作被占用的那个位置原来的数的新的“原位”),有(n−1)Dn−2种方案。
- 把数1放到其他位置上,然后不把该位置原来的数放到1原来的位置上,有(n−1)Dn−1种方案。
所以,Dn=(n−1)(Dn−1+Dn−2),D1=0,D2=1 。
带有禁止位置的排列
没有什么很套路的计算方法,甚至可能使用容斥原理会使计算更加复杂。
带有绝对禁止位置的排列计数问题,在集合之间没有明显规律的时候,使用容斥原理统计可能会比较复杂,使用程序实现容斥原理也很可能是指数级的计算复杂度。
带有
Ai:第i+1个人前面的人是上一次的人
几何问题