排列组合 & 容斥 总结
加法原理
加法原理。很直白的,就是一个用加法来弄的原理。
简单来说,就是做一件事情有 \(n\) 种方法,第 \(i\) 种方法又有 \(a_i\) 个具体的操作方案。那么非常显然,做这件事情就有 \(a_1 + a_2 + \dots + a_{n-1} + a_n = \sum_{i=1}^{n} a_i\) 种方案。
说说具体用途吧:最常见的,DP 里面统计方案数,由于这些解决方案是互不干扰的,也就是说如果选择 \(1\) 类方法就不可能同时选择 \(2\) 类方法,那么很多时候就是把它们加起来啦。
这就是加法原理。
乘法原理
乘法原理,顾名思义,和加法原理一样,当然是用乘法来弄的原理啦!
乘法原理和加法原理不同,它表示的是,做一件事情有 \(n\) 个步骤,第 \(i\) 个步骤又有 \(a_i\) 种具体的操作方案,那么做这件事情一共就有 \(a_1 \times a_2 \times \dots a_{n-1} \times a_n = \prod_{i=1}^{n} a_i\) 中具体的操作方案。
用途吧,就是很多时候考虑统计方案数的时候,要分步骤来考虑——换言之,分类讨论?——总之就是有一个分步骤的阶段的情况下,就要用到乘法原理,把它们乘起来啦。
这就是乘法原理。
全排列
全排列表示有 \(n\) 个互不相同的物品,然后将这些物品全部打乱,重组地去排列,一共有多少种方法。答案显然,\(n \times (n-1) \times (n-2) \times \dots \times 2 \times 1\),定义这个东西为 \(n!\),即 \(n\) 的阶乘。
排列组合
排列,就是指从 \(n\) 个物品中选择 \(m\) 个排成一排。假设它们都互不相同,那么方案数是多少呢?很显然,排在第一个位置有 \(n\) 种选法,第二个位置有 \(n-1\) 种选法,第 \(i\) 个位置有 \(n-i+1\) 种选法,第 \(m\) 个位置有 \(n-m+1\) 种选法,全部乘起来,那就是 \(n \times (n-1) \times (n-2) \times \dots \times (n-m+1)\) 啦!这就定义为 \(A_{n}^{m}\),且有公式 \(A_{n}^{m} = \dfrac{n!}{(n-m)!}\)。
组合,同样是从 \(n\) 个物品中选择 \(m\) 个,但是不同的是,它是选择 \(m\) 个,但是只把这东西当做一个集合,并不对其进行排序——对,差别就在于,它比排列少考虑了具体的排序情况!这东西定义为 \(C_{n}^{m}\),也记做 \(\binom{n}{m}\),且有公式 \(C_{n}^{m} = \binom{n}{m} = \dfrac{n!}{(n-m)!\space{}m!}\)。对!就是比排列多除以了一个 \(m!\) 而已,因为不考虑顺序啦。
组合还有一个公式,那就是 \(C_{n}^{m} = C_{n-1}^{m-1} + C_{n-1}^{m}\),这也是求解杨辉三角时的递推公式。为什么呢?因为我们已经考虑完了前 \(n-1\) 个元素的情况,现在多了一个 \(n\) 元素,我们可以考虑选它,那么之前 \(n-1\) 个元素中就只需要选择 \(m-1\) 个了,这就是 \(C_{n-1}^{m-1}\),也可以考虑不选它,那么之前 \(n-1\) 个元素中还需要选择 \(m\) 个,这就是 \(C_{n-1}^{m}\),加法原理加起来就有公式 \(C_{n}^{m} = C_{n-1}^{m-1} + C_{n-1}^{m}\) 啦。
杨辉三角可以 \(O(n^2)\) 预处理出来,因此当 \(n\) 不大且需要使用组合数的时候常用杨辉三角预处理哦。
容斥
举个简单的例子,全班有 \(20\) 人,其中有 \(10\) 个人喜欢吃巧克力,\(15\) 个人喜欢吃冰淇淋,还有 \(2\) 个人什么都不喜欢吃,问有多少个人既喜欢吃巧克力又喜欢吃冰淇淋呢?很简单,首先考虑把什么都不喜欢吃的 \(2\) 个人减掉,那么还剩 \(20-2=18\) 个人。\(10+15=25\),如果不发生任何重叠的话还有 \(25\) 个人,但这显然和 \(18\) 不吻合,因此拿 \(25 - 18 = 7\),有 \(7\) 个人既喜欢吃巧克力又喜欢吃冰淇淋。
更常见的,比如说维恩图这种,有三个集合 \(A,B,C\),分别的大小都是 \(|A|,|B|,|C|\),问它们的并集 \(A \cup B \cup C\) 的大小即 \(|A \cup B \cup C|\) 是多少。不难得出式子 \(|A \cup B \cup C| = |A| + |B| + |C| - |A \cap B| - |A \cap C| - |B \cap C| + |A \cap B \cap C|\),画图会比较形象一些。如果延伸一下,大概就是,依次枚举出现 \(i\) 个数字,然后交集什么的,加起来,乘上 \((-1)^{i-1}\) 加进答案里。
容斥的运用挺广泛的,很多题目都有它的影子,比如说有时候吧,我们求在满足某某条件下的方案数,但是不好求,就可以改成是全部的方案数减去不满足某某条件下的方案数,这样就能算出来了,这就是正难则反,也是容斥最最基本、最最常见的运用了。有的时候可能要同时满足两个条件,那么就可以改成全部的方案数减去不满足第一种条件的方案数再减去不满足第二种条件的方案数最后再加上两种方案都不满足的方案数。确实,说起来挺绕口的,但这个东西就是上面讲到过的容斥的一个思想,很常用的哈,甚至在某些 DP 啊图论啊什么的题目里,都会融入一点点容斥的元素在里边。
例题选讲
题太多了,我就挑几道重点讲,其他的带过一下好吧。
A - 盒子与球
Tag:阶乘,DP。
定义 \(dp_{i,j}\) 表示考虑将 \(i\) 个球放入 \(j\) 个盒子的情况下有多少种方案。
转移考虑两种情况,一种是将球 \(i\) 新开一个盒子 \(j\) 放入,还有一种是从原先 \(j\) 个盒子中任意挑选一个放入球 \(i\),加起来就是 \(dp_{i,j} = dp_{i-1,j-1} + j \times dp_{i-1,j}\)。
最后的答案就是 \(dp_{n,r}\)……吗?不对,因为这里没有考虑球的顺序情况,而题目明确说明球是互不相同的,因此还要乘上 \(r!\) 才行,所以最终答案是 \(dp_{n,r} \times r!\)。
B - Permutation Warm-Up
Tag:打表找规律(?不是这对吗)。
但是好像就是打个表……然后去研究一下整体的一个规律趋向。定义 \(S(x)\) 表示 $x + (x-2) + (x-4) + \dots $ 直到 \(0\),也就是说当 \(x\) 为奇数的时候 \(S(x) = 1 + 3 + \dots + (x-2) + x\),而当 \(x\) 为偶数的时候 \(S(x) = 2 + 4 + \dots + (x-2) + x\)。答案即为 \(S(n-1) + 1\),为什么要 \(+1\) 呢,因为还有 \(0\) 的情况呀!
C - Having Been a Treasurer in the Past, I Help Goblins Deceive
Tag:贪心,简单结论运用。
首先算一下原 \(s\) 中有多少个眼睛和多少个嘴巴,分别记为 \(Eye\) 和 \(Mou\)。
由于这个 -_- 由中间一个嘴巴和左右分别一个眼睛构成,为了最大化这个图标的数量,考虑把所有嘴巴都放在中间,然后把眼睛放在两边。
易知如果在左边放 \(x\) 个眼睛,右边放 \(y\) 个眼睛(显然有 \(x+y=Eye\))的话,那么就有 \(Mou \times x \times y\) 个 -_-,为了最大化 \(x \times y\),显然考虑平均分 \(Eye\) 分布在左右两边,因此有 \(x = \lfloor \frac{Eye}{2} \rfloor\) 以及 \(y = Eye - \lfloor \frac{Eye}{2} \rfloor\),最后输出就好啦!
D - Replace Character
Tag:桶,贪心,构造。
首先考虑一个字符串会产生多少种排列数。假设 \(c_{\text{a}}\) 表示字符串中 \(\text{a}\) 出现的次数,\(c_{x}\) 表示字符串中 \(x\) 出现的次数,不难发现这个排列数为 \(\dfrac{n!}{c_{\text{a}}!c_{\text{b}}!\dots c_{\text{y}}! c_{\text{z}}!}\)。
由于题目希望操作后的字符串具有最小不同排列数,又因为这个操作等价于将某种字符的个数 \(+1\) 再将某种字符的个数 \(-1\),不难贪心地想到将出现次数最小的字符的个数 \(-1\),再将出现次数最大的字符的个数 \(+1\),这样能最大化上面多 \(\div\) 的值,因为如果将某种出现次数为 \(x\) 的字符的个数 \(+1\) 再将某种出现次数为 \(y\) 的字符的个数 \(-1\),会将原来的排列数 \(\times \dfrac{y}{x+1}\),为了最小化 \(\dfrac{y}{x+1}\),当然是选择最大的 \(x\) 且最小的 \(y\) 啦。
因此用桶统计一下每种字母出现的次数,然后挑出最大最小进行一个修改,最后输出就可以啦。
E - 数字串个数
Tag:快速幂,容斥。
由于这个人不喜欢数字 \(0\),因此每一位都只能出现 \(1 \sim 9\),共 \(9\) 种数字,那么一共就有 \(9^{10000}\) 种情况。
由于必须存在 \(3\),因此减去没有 \(3\) 的情况数 \(8^{10000}\);同样由于必须存在 \(7\),因此减去没有 \(7\) 的情况数 \(8^{10000}\)。插一嘴题外话,这两个数字正好也是我喜欢的,不过少了一个 \(5\)。
但是这样的话既不存在 \(3\) 又不存在 \(7\) 的数字就被减了两次了,要补回来一次,因此最后还要加上 \(7^{10000}\)。
所以答案为 \(9^{10000} - 8^{10000} - 8^{10000} + 7^{10000}\),输出即可。
F - 越狱
Tag:快速幂,容斥。
首先考虑所有关押的情况数,有 \(m^n\) 种。
接着考虑不会越狱的情况数——首先为第一个房间的人挑选一个宗教,有 \(m\) 种情况,那么接下来 \(n-1\) 个人为了不和前一个人同样就只有 \(m-1\) 种选择了,那么情况数就是 \(m \times (m-1)^{n-1}\)。
对这两个东西做减法,\(m^n - m \times (m-1)^{n-1}\) 就是答案。
G - 炼金术(Alchemy)
Tag:快速幂,简单思维。
插一嘴题外话,oymz 真的太强了,请教一下如何做到猜样例猜对结论的。
考虑每一种金属元素分别出自哪个熔炉。很显然,有多个熔炉炼出这种金属元素是没有问题的,但是不能没有熔炉炼出这种金属元素。不难发现有 \(2^k - 1\) 种炼出金属的熔炉的选择方案——用二进制来考虑,\(1\) 表示炼出,\(0\) 表示没有炼出。而因为有 \(n\) 种金属元素,因此答案就是 \((2^k-1)^n\) 啦,写个快速幂就完事儿了。
H - Kuro and Walking Route
Tag:分类讨论,DFS,容斥。
首先跑一通 DFS,算出每个子树的大小 \(sz_u\)。为了方便直接令 \(1\) 为根。
首先考虑最简单的——\(X\) 和 \(Y\) 两个点不构成任何祖先啥的关系,也就是说,\(X\) 不是 \(Y\) 的祖先,\(Y\) 也不是 \(X\) 的祖先,的情况下——很显然,答案就是 \(n \times (n-1) - sz_X \times sz_Y\)。因为只有从 \(X\) 的子树出发,到达 \(Y\) 的子树,才会经过 \(X\) 和 \(Y\) 这两个点,因此简单容斥即可。
接着考虑存在祖先关系的。这里只说明 \(X\) 是 \(Y\) 的祖先的情况,反过来是类似的。由于 \(X\) 是 \(Y\) 的祖先,因此肯定是要从外面什么地方进来,首先要进到 \(X\) 这棵子树,然后再进入 \(Y\) 的子树的情况。但是我们发现,答案 \(n \times (n-1) - (n-sz_X+1) \times sz_Y\) 是不对的,因为也有可能是本来就在 \(X\) 的子树,但和 \(Y\) 不处于同一分叉的情况下,也是会先进 \(X\) 再到 \(Y\) 的,因此要找到 \(Y \to X\) 的这条路上经过的倒数第二个节点,也就是 \(X\) 的儿子节点中唯一一个是 \(Y\) 的祖先的节点 \(K\),答案 \(n \times (n-1) - (n-sz_K) \times sz_Y\) 才是正确答案。
I - 启 · 破茧初阳
Tag:容斥,最大公因数和最小公倍数。
首先考虑一下——只是 \(a\) 的倍数的,只是 \(a\) 和 \(c\) 的倍数的,只是 \(b\) 和 \(c\) 的倍数的,只是 \(a\) 和 \(b\) 和 \(c\) 的倍数的,都要算进来——换句话说,只有只是 \(a\) 和 \(b\) 的倍数的,以及只是 \(b\) 的倍数的,被无情地拦在了门外不能被算进去。
这个东西画个图可能会形象一点啊,这里就文字描述了。其实是因为懒得画图 emm
那么怎么算呢?这里首先定义 \(S(x)\) 表示是 \(x\) 的倍数的数字个数,\(S(x,y)\) 表示既是 \(x\) 的倍数又是 \(y\) 的倍数的数字个数,同理定义 \(S(x,y,z)\) 表示既是 \(x\) 的倍数又是 \(y\) 的倍数还是 \(z\) 的倍数的数字个数,方便以下进行描述。
首先让 \(Ans = S(a) + S(c)\),但是这样显著多了,\(a\) 和 \(b\),以及 \(a\) 和 \(c\) 的,都要减去,因此 \(Ans = S(a) + S(c) - S(a,b) - S(a,c)\)。但是这样的话 \(a\) 和 \(b\) 和 \(c\) 的就被减没了,因此还要加上 \(S(a,b,c)\) 才是最终的正确答案。
但是 \(S(x),S(x,y),S(x,y,z)\) 究竟都该怎么做呢?其实就是对这传进来的几个参数求个 \(\text{lcm}\),那么就是 \(\lfloor \dfrac{n}{\text{lcm}} \rfloor\) 了。
题目很坑,三个数的 \(\text{lcm}\) 用 long long 存不下,要开 __int128。
J - Twin Polynomials
Tag:DP。
是的,我觉得这就是一个 DP 的一个思想。
首先考虑所有 \(a_i = -1\),也就是所有 \(a_i\) 都没有被定下来的情况。\(a_0\) 是不用考虑的,因此我们只考虑 \(a_{1 \sim n}\)。
发现一个式子,有 \(a_i = \sum_{a_j = i} j\),并且对于所有 \(1 \le i \le n\),最多也只能有一个 \(j\) 可以和它组成一个环组,也就是 \(a_i = j\) 且 \(a_j = i\)。
对于每个 \(a_i (1 \le i \le n)\),都只有三种取值,要么 \(a_i = 0\),要么 \(a_i = i\),要么存在一个 \(j\) 使得 \(a_i = j\) 且 \(a_j = i\)。
定义 \(dp_i\) 表示当前考虑 \(i\) 个数进行匹配的情况。枚举 \(i\),考虑转移。首先有这个值等于 \(0\) 或者自己本身的情况,那么有 \(dp_{i-1} \times 2\) 种情况;还有一种方法那就是从前面 \(i-1\) 个数字种选择一个来和 \(i\) 组成环组,那么有 \(dp_{i-2} \times (i-1)\) 种情况;加起来得到转移方程 \(dp_i = dp_{i-1} \times 2 + dp_{i-2} \times (i-1)\)。
现在考虑只存在部分 \(a_i = -1\) 的常规情况,首先把需要匹配的环组情况都搞定,发现不合法的直接输出 \(0\)。
接着统计在 \(a_1 \sim a_{n-1}\) 中 \(-1\) 的数量,记为 \(cnt\),考虑最终答案是多少。如果 \(a_n \not= -1\),也就是说 \(a_n\) 的值已经被固定了(可能是前面有值和它形成了环组),那么答案就是 \(dp_{cnt}\);否则的话,还要考虑上 \(a_n\) 的情况——但答案可不是 \(dp_{cnt} \times 2 + dp_{cnt-1} \times cnt\) 而是 \(dp_{cnt} + dp_{cnt-1} \times cnt\),为什么?因为题目明确说了,\(a_n\) 不能等于 \(0\),因此就少一种情况啦!
时刻注意取模,不然会炸完。
总结
排列组合常用来出偏数论、数学这一块的题目,也有些时候会融入 DP 或者图论什么的元素,有时会用杨辉三角预处理组合数;并且很多需要使用组合数的题目,基本都和乘法逆元脱不了干系。而容斥的运用则更加广泛,不仅在数论这个板块频繁显身,很多时候都会隐藏在各种各样的算法题目中——比如最经典的“正难则反”,有哪些没用到容斥?因此容斥是一个经常出现的东西,不过这里有一个小小的坑,如果说要做减法但是取了模的话,一定要记得先加上模数再取一次模,不然可能会出现负数哦!
Thanks reading.

浙公网安备 33010602011771号