组合数学(未完结)
组合数学
组合数学应该是 OI 中数学板块里内容最多,最重要的一部分了吧。感觉组合数学也是最有意思的。相信可以总结的点一定很多(确信)。希望我的总结不要又臭又长。
计数原理
加法原理
若完成一件事的方法有 \(n\) 类,其中第 \(i\) 个步骤包括 \(a_i\) 种不同的方法,且这些方法互不重合,则完成这件事共有 \(a_1+a_2+\cdots+a_n\) 种不同的方法。
乘法原理
若完成一件事需要 \(n\) 个步骤,其中第 \(i\) 个步骤有 \(a_i\) 种不同的完成方法,且这些步骤互不干扰,则完成这件事共有 \(a_1 \times a_2 \times \cdots \times a_n\) 种不同的方法。
排列组合
排列数
从 \(n\) 个不同元素中依次取出 \(m\) 个元素排成一列,产生的不同排列的数量为:
组合数
从 \(n\) 个不同元素中取出 \(m\) 个组成一个集合(不考虑顺序),产生的不同集合数量为:
基本性质
-
\(\mathrm C_n^m=\mathrm C_n^{n-m}\)
展开组合数公式即可。
-
\(\mathrm C_n^m=\mathrm C_{n-1}^m+\mathrm C_{n-1}^{m-1}\)
证明:
从 \(n\) 个元素中取 \(m\) 个元素有两类方法:取 \(n\) 号元素和不取 \(n\) 号元素。
若取 \(n\) 号元素,则需要在剩下 \(n-1\) 个元素中取 \(m-1\) 个,即 \(\mathrm C_{n-1}^{m-1}\) 种;
若不取 \(n\) 号元素,则需要在剩下 \(n-1\) 个元素中取 \(m\) 个,即 \(\mathrm C_{n-1}^m\) 种。
然后加法原理即可。
证毕。
这条性质同样也可以看做是杨辉三角。
-
\(\mathrm C_n^0+\mathrm C_n^1+\mathrm C_n^2+\cdots+\mathrm C_n^n=2^n\)
证明:
从 \(n\) 个不同元素中取出若干个元素组成一个集合,有 \(n+1\) 类方法,分别是取出 \(0,1,2,\cdots,n\) 个。由加法原理可知,共有 \(\mathrm C_n^0+\mathrm C_n^1+\mathrm C_n^2+\cdots+\mathrm C_n^n\) 种方法。
也相当于是 \(n\) 个元素中每个元素都可以取或者不去,方案数为 \(2^n\)。
二者相等。
证毕。
二项式定理
证明:
数学归纳法。
当 \(n=1\) 时,\((a+b)^1=\mathrm C_n^0a^0b^1+C_n^1a^1b^0=a+b\) 成立。
假设当 \(n=m\) 时命题成立,当 \(n=m+1\) 时:
证毕。
组合数求和
-
对角线求和:\(\mathrm C_n^m+\mathrm C_{n+1}^{m+1}+\cdots+\mathrm C_{n+k}^{m+k}=\mathrm C_{n+k+1}^{m+k}+\mathrm C_n^{m-1}\)
-
直线求和:\(\mathrm C_n^m+\mathrm C_{n+1}^m+\cdots+\mathrm C_{n+k}^m=\mathrm C_{n+k+1}^{m+1}-\mathrm C_n^{m+1}\)
组合数求法
-
递推法
根据基本性质 2,通过杨辉三角递推求组合数。
时间复杂度:\(O(n^2)\)。
此方法主要适用于预处理。
代码实现:
C[0][0]=1;//upd on 2022.7.18:一般来说不会用到这个,但是我在某场模拟赛补题中因为这个 WA 75pts 了。 for (int i=1;i<=n;i++) for (int j=0;j<=i;j++) { if (i==1 || j==0) C[i][j]=1; else C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; }
-
乘法逆元
此方法主要用于结果要求模大素数时。比如我们要求 \(\mathrm C_n^m \bmod p\):
因为 \(\mathrm C_n^m=\frac {n!}{m!(n-m)!}\),所以我们可以预处理出来 \(n! \bmod p\) 以及 \(m! \bmod p\) 和 \((n-m)! \bmod p\) 的逆元,然后三者相乘即为答案。
时间复杂度:\(O(n)\)。
代码实现:
void init()//预处理 { jc[0]=jc[1]=inv[0]=inv[1]=in[1]=in[0]=1; for(int i=2;i<=n;i++) { jc[i]=jc[i-1]*i%mod;//阶乘 inv[i]=(mod-mod/i)*inv[mod%i]%mod;//inv[] 是逆元。 in[i]=in[i-1]*inv[i]%mod;//in[] 表示阶乘逆元。 } } int C(int n,int m){return jc[n]*in[m]%mod*in[n-m]%mod;}
-
质因数分解
因为 \(\mathrm C_n^m=\frac {n!}{m!(n-m)!}\),所以可以对分子,分母快速分解质因数,在数组中保存各项质因子的质数。然后把分子,分母各个质因子的指数对应相减(把分母消去),最后把剩余质因子乘起来即可。
时间复杂度:\(O(n \log n)\)。
Lucas 定理
若 \(p\) 是质数,则对于任意整数 \(1 \le m \le n\),有:
也就是把 \(n\) 和 \(m\) 表示成 \(p\) 进制数,对 \(p\) 进制下的每一位分别计算组合数,最后再乘起来。
本来是不打算写证明的,因为这个证明也不需要掌握,但是我感觉它非常有意思,所以就把它写在这里吧。
证明:
咕咕咕……(组合数学完全整理完之前一定过来补上!)
证毕。
代码实现:
int lucas(int n,int m,int p)
{
if(m==0)return 1;
return (C(n%p,m%p,p)*lucas(n/p,m/p,p))%p;
}
可重集的排列数
可重集,指的是包含重复元素的广义集合。
\(n\) 种字母,每种字母 \(a_i\) 个,有 \(\frac {n!}{\prod(a_i!)}\) 种不同的排列。
可重集的组合数
设 \(S=\{n_1 \times a_1,n_2 \times a_2,\cdots,n_k \times a_k\}\) 是由 \(n_1\) 个 \(a_1\),\(n_2\) 个 \(a_2\),…,\(n_k\) 个 \(a_k\) 组成的可重集。
设整数 \(r \le n_i(\forall i \in [1,k])\)。从 \(S\) 中取出 \(r\) 个元素组成一个可重集(不考虑元素的顺序),产生的不同可重集的数量为 \(\mathrm C_{k+r-1}^{k-1}\)。
证明:
原问题等价于求下列集合的数量:\(\{x_1 \times a_1,x_2\times a_2,\cdots,x_k\times a_k\}\),其中 \(\sum \limits_{i=1}^k x_i=r(x_i\le n_i)\)。
原问题等价于 \(r\) 个 0,\(k-1\) 个 1 构成的全排列数——\(k-1\) 个 1 把 \(r\) 个 0 分成 \(k\) 组,每组的 0 的数量对应的是 \(x_i\)。
也可以看做总共有 \(k+r-1\) 个元素,然后我们要选择其中 \(k-1\) 个作为隔板,求总的方案数。
而可重集 \(\{r \cdot 0,(k-1) \cdot 1\}\) 的全排列个数为:
证毕。
不相邻的排列
\(1-n\) 这 \(n\) 个自然数中选 \(k\) 个,这 \(k\) 个数中任何两个数都不相邻的组合有 \(\mathrm C_{n-k+1}^k\) 个。
证明:
插空法。利用逆向思维,先从 \(n-k+1\) 个数中任选 \(k\) 个数,共 \(\mathrm C_{n-k+1}^k\) 种方案。然后再把 \(k-1\) 个数当做空位插入到 \(k\) 个数的 \(k-1\) 个空中,即为 \(n\) 个自然数。
证毕。
错位排列
将 \(1-n\) 这 \(n\) 个自然数生成全排列,其中第 \(i(\forall 1\le i \le n)\) 位不能为 \(i\) 的方案数被称为错位排列,简称错排,记为 \(D_n\)。
我们分两步考虑递推关系:
-
第一步,考虑第 \(n\) 个元素,把它放在在某一个位置,比如位置 \(k\),一共有 \(n-1\) 种放法;
-
第二步,考虑第 \(k\) 个元素,这时有两种情况:
-
把它放到位置 \(n\),那么对于除 \(n\) 以外的 \(n-1\) 个元素,由于第 \(k\) 个元素放到了位置 \(n\),所以考虑剩下 \(n-2\)个元素的错排即可,有 \(D_{n-2}\) 种放法;
-
第 \(k\) 个元素不放到位置 \(n\),这时对于这 \(n-1\) 个元素的错排,有 \(D_{n-1}\) 种放法。
-
根据乘法原理和加法原理可知:
另外:
这一点是根据容斥原理推出来的。
也就是说,\(n\) 的错排等于 \(n\) 的全排列,减去至少有一个数不是错排的方案数,加上至少有两个数不是错排的方案数,减去至少有三个数不是错排的方案数……
注意,容斥原理是总方案数-不合法方案数,这里的“不是错排”就是“错排”的反义,所以不是错排的方案数就是不合法方案数。
错位排列前几项:\(0,1,2,9,44,265\)。
圆排列
\(n\) 个人全部来围成一圈,所有的排列数记为 \(Q_n^n\)。
考虑其中已经排好的一圈,从不同位置断开,变成不同的队列。所以有:
那么部分圆排列的方案数(即 \(n\) 个人中挑出 \(r\) 个人围成一圈)为:
另外,将 \(n\) 个不同的数构成 \(m\) 个圆排列的数目被称为第二类斯特林数,后面会专门说到斯特林数,这里不再赘述。
放球问题
最全面的放球问题请参考放球问题 学习笔记 - ACodinghusky - 博客园,这里只讨论最常见的 8 种情况。
在考虑这个问题之前,我们先解释一个问题:球同和球异,盒同和盒异到底有什么区别?
说实话,我其实刚开始一直是糊涂的,知道看到 dbxxx 的博客里面的一段话才理解:
球盒区别解释
假设n=3,m=2,对球编号 1,2,3:
为方便,规定类似于如下的表示:
{1, 2}, {3}
表示第一个盒子中有1,2两个球,第二个盒子中有3一个球而且无论十六种问题中哪一种,对于单个盒子而言,即使球有区别,盒子内部球的顺序永远只算一种情况。也就是说
{1, 2}, {3}
和{2, 1}, {3}
只能算作一种情况(无论 16 种问题的哪一个。)如果球有区别,盒也有区别,那么除了上一句说的情况外,其他所有的都各自算各自的情况。比如:
{1, 2}, {3}
,{3}, {1, 2}
,{1, 3}, {2}
这三种情况每一种都是不同的。如果球无区别,意味着将来自两个盒子的两个球进行交换是算作一种情况的。比如:
{1, 2}, {3}
和{1, 3}, {2}
是一种情况。如果盒无区别,意味着将任意两个盒子调换位置是算作一种情况的。比如:
{1, 2}, {3}
和{3}, {1, 2}
是一种情况。那么如果球和盒都没有区别……
{1, 2}, {3}
,{3}, {1, 2}
,{1, 3}, {2}
,{2, 3}, {1}
,{2}, {1, 3}
等都是一种情况。在球和盒都没有区别的模型下,还可以创造新的情况吗?
{}, {1, 2, 3}
,可以将一个盒子清空,这样就可以创造另一种情况了。同理,{1, 2, 3}, {}
和{}, {1, 2, 3}
是一种情况,因为盒子没有区别。在球和盒都没有区别的模型下,必须有空的集合才能有别的情况吗?当然不是。不妨考虑扩展到 4 个球。那么:
{}, {1, 2, 3, 4}
,{1}, {2, 3, 4}
,{1, 2}, {3, 4}
就是三种情况。
这里必须 %%% dbxxx,真的是把这个问题总结的非常全面详细。大家有兴趣可以去看看上面我放的他的博客。
假设有 \(n\) 个球和 \(m\) 个盒子,那么有如下 4 类 8 种情况:
-
球异,非空
-
盒同:
实际上就是第二类斯特林数,因此答案是 \(S(n,m)\)。
第二类斯特林数的求法下面会提到,这里不做赘述。
-
盒异:
就相当于是计算出上述盒同的所有情况后给盒子编号,所以需乘上盒子的全排列。
所以答案是 \(m!\times S(n,m)\)。
-
-
球异,可空
-
盒异:
每个球都有 \(m\) 种放法,那么 \(n\) 个球就是 \(n\) 个 \(m\) 相乘,也就是 \(m^n\) 种放法。
-
盒同:
和 1 中的第一种情况的区别在于,盒子可以空放了。
那么枚举空盒个数累计到第二类斯特林数即可。因此,答案为 \(\sum \limits_{k=1}^m S(n,k)\)。
-
-
球同,非空
-
盒异:
隔板法。题目相当于把 \(n\) 个球分成 \(m\) 段,那么需要 \(m-1\) 个隔板。
而 \(n\) 个球之间有 \(n-1\) 个缝隙。所以问题就转化为从 \(n-1\) 个缝隙中找到 \(m-1\) 个插进去。
所以答案为 \(\mathrm C_{n-1}^{m-1}\)。
-
盒同:
请先移步到 4 第二种情况。在那一种情况基础之上,当遇到非空,我们强制给每个盒子一个球转化为可空,方案数就为 \(dp_{n-m}{m}\)。
-
-
球同,可空
-
盒异:
同样和 3 中的第一种情况的区别在于,盒子可以空放了。
我们运用化归思想,把可空问题转化为非空问题。
先构造 \(n+m\) 求 \(m\) 盒和 3 中的第一种情况一样的问题。
然后在所有方案中,我们给每个盒子里面去掉一个球,这样球的总数还是 \(n\) 个,方案也就转化成了可空的问题。
也就是说可空的 \(n\) 球 \(m\) 盒问题,可以转化为不可空的 \(n+m\) 球 \(m\) 盒问题。
那么最后答案就是 \(\mathrm C_{m+n-1}^{m-1}\)。
怎么样?是不是很巧妙?
-
盒同:
实际上要求的是长度为 \(m\) 的有序数列个数,数列的元素和为 \(n\)。
记 \(dp_{n,m}\) 为划分方案数,有\[dp_{0,k} = dp_{k,1} = 1\\ dp_{i,j} = dp_{i − j,j} + dp_{i,j − 1} \]状态转移方程解释:考虑 \(j\) 个盒子中是否有空盒
-
有空盒:新增一个空盒,贡献 \(dp_{i,j-1}\);
-
无空盒:向每个盒子放入一个球使其非空,贡献 \(dp_{i-j,j}\);
-
-
两类特殊的数
卡特兰数
给定 \(n\) 个 0 和 \(n\) 个 1,它们按照某种顺序排成长度为 \(2n\) 的序列, 满足任意前缀中 0
的个数都不少于 1 的个数的序列的数量为:
证明:
令 \(n\) 个 0 和 \(n\) 个 1 随意排成一个长度为 \(2n\) 的序列 \(S\),若 \(s\) 不满足任意前缀中 0 的个数都不少于 1 的个数,那么一定存在一个最小的位置 \(2p+1\in [1,2n]\),使得 \(S[1 \thicksim 2p+1]\) 中有 \(p\) 个 0,\(p+1\) 个 1。
所以此时,\(S[2p+2 \thicksim 2n]\) 中含有 \(n-p-1\) 个 1 和 \(n-p\) 个 0,,所以把 \(S[2p+2 \thicksim 2n]\) 中的所有数字取反以后,包含 \(n-p-1\) 个 0 和 \(n-p\) 个 1。加上前 \(2p+1\) 个,于是我们得到了由 \(n-1\) 个 0 和 \(n+1\) 个 1 排成的序列。
同理,令 \(n-1\) 个 0 和 \(n+1\) 个 1 随意排成一个长度为 \(2n\) 的序列 \(S'\),也必定存在一个最小的位置 \(2p'+1\),使得 \(S'[1 \thicksim 2p'+1]\) 中有 \(p'\) 个 0,\(p'+1\) 个 1。把 \(S'\) 后面剩下的一半取反,就得到了由 \(n+1\) 个 0 和 \(n\) 个 1 排成的,存在一个前缀 0 比 1 多的序列。
因此,以下两种序列构成了一个双射:
-
由 \(n\) 个 0 和 \(n\) 个 1 排成的,存在一个前缀 0 比 1 多的序列;
-
由 \(n-1\) 个 0 和 \(n+1\) 个 1 排成的序列。
根据组合数的定义,后者显然有 \(\mathrm C_{2n}^{n-1}\) 个。
综上,由 \(n\) 个 0 和 \(n\) 个 1 排成的,任意前缀中 0 的个数都不少于 1 的个数的序列的数量为:
证毕。
推论
-
\(n\) 个左括号和 \(n\) 个右括号组成的合法括号序列的数量为 \(Cat_n\);
-
\(1-n\) 这 \(n\) 个自然数依次进栈,形成的合法的出栈序列的数量为 \(Cat_n\);
-
\(n\) 个节点构成的不同二叉树的数量为 \(Cat_n\);
-
凸 \(n+2\) 边形过顶点切分成 \(n\) 个三角形的不同方案数;
-
在平面直角坐标系上,每一步只能向上或向右走,从 \((0,0)\) 走到 \((n,n)\) 并且除两个端点之外不接触直线 \(y=x\) 的路线数量是 \(2Cat_{n-1}\);
-
给定 \(n\) 个 0 和 1,其中 \(m\) 个为 0,其余是 1 的 Catalan 数为:
\[\mathrm C_n^m-\mathrm C_n^{m-1} \]或者给定 \(n+m\) 个 0 和 1,其中 \(m\) 个为 0,\(n\) 个为 1 的 Catalan 数为:
\[\mathrm C_{n+m}^m-\mathrm C_{n+m}^{m-1} \]物理意义:在平面直角坐标系上,每一步只能向上或向右走,从 \((0,0)\) 走到 \((n,m)\) 并且除两个端点之外不接触直线 \(y=x\) 的路线数量。
斯特林数
写在后面
post on 2022.4.19
组合数学这个我保证在完全结束这一板块的学习之前,就把所有该总结的都总结完。
一是这个版块的确是太重要了,另外咕咕咕并非一个好的习惯。
现在才有三千多字,后面可能会成倍成倍的往上增长(不是)。
感觉组合数学真的非常有意思,而且让人回味无穷(确信),但是这仍然改变不了我菜的事实。
说起来多多少少和组合数学有点渊源,毕竟,我在洛谷上发的第一篇题解就是组合数学,我 AC 的第一道黑题就是组合数学,我在洛谷上提交次数最多的题目也还是道组合数学。
艾教说,组合数学学好了整个 dp 就会串起来,就会变得非常强。
所以我会尽力把组合数学这块学好的。
To be continued……