约定:如非特殊说明,本文所涉及的数均为非负整数.
基本概念
阶乘
对于任意非负整数 n,称 n×(n−1)×(n−2)×⋯×3×2×1 为 n 的阶乘,记作 n!,即
n!=n×(n−1)×(n−2)×⋯×3×2×1.
特别地,0!=1.
分类加法计数原理
完成一件事有两类不同方案,在第 1 类方案中有 m 种不同的方法,在第 2 类方案中有 n 种不同的方法,那么完成这件事共有
N=m+n
种不同的方法.
分步乘法计数原理
完成一件事需要两个步骤,做第 1 步有 m 种不同的方法,做第 2 步有 n 种不同的方法,那么完成这件事共有
N=m×n
种不同的方法.
排列
从 n 个不同元素中取出 m(m≤n)个元素,并按照一定的顺序排成一列,叫做从 n 个不同元素中取出 m 个元素的一个排列.
两个排列相同的充要条件是:两个排列的元素完全相同,且元素的排列顺序也相同.
从 n 个不同元素中取出 m(m≤n)个元素的所有不同排列的个数,叫做从 n 个不同元素中取出 m 个元素的排列数,用符号 Anm 表示(A 是 arrangement 的第一个字母).
对于 Anm 的计算,我们可以将其考虑为依次填 m 个空位:
- 第 1 位可以填 n 种;
- 第 2 位可以填 (n−1) 种;
- 第 3 位可以填 (n−2) 种;
- ……
- 第 m 位可以填 (n−m+1) 种.
根据分步乘法计数原理,总的填法种数为 n(n−1)(n−2)⋯(n−m+1).
所以
Anm=n(n−1)(n−2)⋯(n−m+1)=(n−m)!n!.
特别地,n 个不同的元素全部取出的一个排列,叫做 n 个元素的一个全排列,即 n=m,不难得到
Ann=n!.
组合
从 n 个不同元素中取出 m(m≤n)个元素为一组,叫做从 n 个不同元素中取出 m 个元素的一个组合.
从排列与组合的定义可以知道,两者都是从 n 个元素中取出 m(m≤n)个元素,但排列与元素的顺序有关,而组合与元素的顺序无关.所以可知两个组合相同的充要条件是两个组合的元素完全相同.
从 n 个不同元素中取出 m(m≤n)个元素的所有不同组合的个数,叫做从 n 个不同元素中取出 m 个元素的组合数,用符号 Cnm 或 (mn) 表示(C 是 combination 的第一个字母,下文中简洁直观起见,采用第 2 种写法).
对于 (mn) 的计算,考虑求 Anm 的过程,可以分为
- 从 n 个元素中选出 m 个元素作为一组,共 (mn) 种;
- 将取出的 m 个元素作全排列,共 Amm 种.
根据分步乘法计数原理,可得
Anm=(mn)⋅Amm
即
(mn)=AmmAnm=m!(n−m)!n!.
组合数的简单性质
组合数具有很多优美的性质,这些性质对于加深对计数原理的理解和解决更加复杂的问题有很大帮助.
组合等式的证明通常有组合意义和代数推导两种方式.
下面会给出若干等式,较为显然的代数推导将会略过,请读者自行验证.
(mn)=(n−mn).(1)
从 n 个不同元素中取出 m 个元素后,必然剩下 (n−m) 个元素,因此 n 个不同元素的取出 m 个元素的组合总是能与剩余的 (n−m) 个元素的组合一一对应.
(mn+1)=(mn)+(m−1n).(2)
从 n+1 个不同元素中取出 m 个元素,相当于直接从前 n 个元素中取出 m 个元素,或者先从前 n 个元素中取出 m−1 个元素,再把第 n+1 个元素加入,由分类加法计数原理可得.
m(mn)=n(m−1n−1).(3)
这个式子叫做吸收恒等式.
从 n 个不同元素中取出 m 个元素,并从这 m 个元素中选出 1 个代表,共 m⋅(mn) 种方案.
也可以先从 n 个不同元素中选出 1 个代表,再从剩余 n−1 个不同元素中选出剩余 m−1 个元素,共 n⋅(m−1n−1) 种方案.二者写成等式后移项即可得到 (3) 式.
i=0∑n(in)=2n.(4)
含有 n 个元素的集合的子集数.
(mn)(km)=(kn)(m−kn−k).(5)
从 n 个不同元素中先取出 m 个元素,再从这 m 个元素中取出 k 个元素.也可以先从 n 个人中取出 k 个元素,再从剩余 n−k 个元素中取出 m−k 个元素以补齐 m 个元素.
i=m∑n(mi)=(m+1n+1).(6)
这个式子叫做上指标求和公式.
证明考虑将求和式的第一项 (mm) 改为与之相等的 (m+1m+1),然后不断将前两项用 (2) 式合并,最终可以得到 (m+1n+1).
i=0∑ni(in)=n2n−1.(7)
由 (3) 式:
i(in)=n(i−1n−1).
令 j=i−1,有:
i=0∑ni(in)=nj=0∑n−1(jn−1)=n2n−1.
这些等式对于复杂的组合式化简也有着重要作用,一些应用将会在后文展开描述.
隔板法
问题一:
现有 n 个相同的球和 m 个不同的盒子(n≥m),将这 n 个球放入 m 个盒子中,每个盒子至少放 1 个球,有多少种方法?
考虑将 m−1 块隔板放在 n 个元素形成的 n−1 个空隙内,每个空隙最多放一块隔板,这样我们就实现了一个合法的分配.这种方式显然可以实现任意一种合法分配.所以原问题的答案即为
(m−1n−1).
问题二:
在问题一的基础上,每个盒子允许为空.如果沿用刚才的方法,每个空隙中就可能要放多个隔板,计算困难.考虑转化为问题一,先借 m 个球,在 n+m−1 个空隙里放隔板.则答案为
(m−1n+m−1)=(nn+m−1).
在放完板后,我们从每一个盒子里再拿走 1 个球,即可得到一个合法的问题二的方案.因为所有球都相同,所以这和转化前的情况可以一一对应.
问题三:
在问题一的基础上,第 i 个盒子至少放 ai 个球(∑ai≤n).
类比问题二的解法,先借 ∑ai 个球,问题就转化为了问题二,答案为
(n−∑ain−∑ai+m−1).
二项式定理
(x+y)n=i=0∑n(in)xn−iyi.(8)
(8) 式即为二项式定理.其中 x,y 可以是复数,甚至可以是多项式.各项的系数 (in) 叫做二项式系数.
证明一:
原式即为 n 个 (x+y) 相乘,二项式展开事实上是从每个 (x+y) 中选择一个 x 或 y.
接下来考虑 xn−iyi 这一项,即在 n 个 (x+y) 中选出 i 个组成 yi,剩余 n−i 个组成 xn−i,不难得出这一项的系数应为 (in).
证明二:
考虑数学归纳法.
n=1 时,等式左边为 x+y.等式右边为 (01)x1y0+(11)x0y1=x+y.等式成立.
假设 n=k 时等式成立,即:
(x+y)k=i=0∑k(ik)xk−iyi.
当 n=k+1 时:
(x+y)k+1=(x+y)⋅(x+y)k=(x+y)i=0∑k(ik)xk−iyi=i=0∑k(ik)xk−iyi+1+i=0∑k(ik)xk−i+1yi=i=0∑k+1(ik+1)xk+1−iyi.
其中,最后一步推导利用了上文提到的 (2) 式用于对其下标和合并.
故得证.
令 x=1,y=−1 可得
i=0∑n(−1)i(in)=[n=0].(9)
[] 是艾弗森括号,即 n=0 是原式等于 1,否则等于 0.
由此可得,n>0 时二项式系数的奇数项之和等于偶数项之和.
杨辉三角
将二项式系数排成三角形,这样的图形就是杨辉三角.

由杨辉三角,我们可以重新理解一下上文提到的若干式子.
杨辉三角是轴对称图形,所以 (1) 式成立.
杨辉三角每个元素等于其上方两个元素之和,所以 (2) 式成立.这也是计算杨辉三角的基本方法.
杨辉三角第 i 行之和即为 (4) 式.第 i 列前缀和即为 (6) 式.
杨辉三角还有一个有趣的性质:将三角形左端对齐后,沿右斜 45 度对角线方向取得数之和为斐波那契数.但这个性质对于计数问题意义不大,这里不做进一步探讨.
多重集的排列数
多重集是包含重复元素的广义集合.S={n1⋅a1,n2⋅a2,⋯,nk⋅ak} 表示由 n1 个 a1,n2 个 a2,…,nk 个 ak 组成的多重集.若令 n=n1+n2+⋯+nk,即 n 为集合中元素总数,S 的全排列个数为:
n1!n2!⋯nk!n!=∏i=1kni!n!.
相当于首先将 n 个元素做全排列,然后通过除 ni! 消除第 i 种元素之间的顺序性.这就是多重集的排列数,也叫多重组合数,记作 (n1,n2,⋯,nkn).即
(n1,n2,⋯,nkn)=∏i=1kni!n!.
以下是多重集排列数的性质:
组合数可以与之对应:
(mn)=(m,n−mn).
没什么特殊含义,只是换个写法而已.
多项式定理:
(x1+x2+⋯+xm)n=k1+k2+⋯+km=n∑(k1,k2,⋯,kmn)x1k1x2k2⋯xmkm.
与二项式定理类似,多项式定理的 xi 可以是复数.事实上,二项式定理就是多项式定理在 m=2 的特例.可以类比二项式定理的组合意义证法或对 m 归纳证明,这里不展开介绍.
范德蒙德卷积
i=0∑k(in)(k−im)=(kn+m).(10)
这个式子叫做范德蒙德卷积.
证法一:
注意到
(x+1)n(x+1)m=(x+1)n+m.
等号左边可变为:
(x+1)n(x+1)m=(i=0∑n(in)xi)(j=0∑m(jm)xj)=k=0∑n+m(i=0∑k(in)(k−im))xk.
等号右边可变为:
(x+1)n+m=k=0∑n+m(kn+m)xk.
比较 xk 的系数即证.
证法二:
从大小为 n+m 的集合中选出 k 个元素,等式两边分别表示:
-
将集合分为大小分别为 n,m 且互补的两子集,从大小为 n 的子集中选 i 个,再从大小为 m 的子集中选 k−i 个,考虑 i=0,1,2,⋯,k 的情况.
-
直接选.
容斥原理
对于任意个有限集合 S1,S2,⋯,Sn,它们的并集的大小满足:
i=1⋃nSi=m=1∑n(−1)m−1ai<ai+1∑i=1⋂mSai
更加容易理解的一个形式是这样的:
i=1⋃nSi=i=1∑n∣Si∣−1≤i<j≤n∑∣Si∩Sj∣+1≤i<j<k≤n∑∣Si∩Sj∩Sk∣−⋯
其中第 i 项的符号为 (−1)i+1.
证明一:
若元素 x 属于 S1,S2,⋯,Sn 中的 m 个集合(0<m≤n),其在等式左边被计数 1 次,在等式右边被计数
(1m)−(2m)+⋯+(−1)m+1(mm)
次.由 (9) 式,原式可化为
(0m)−i=0∑m(−1)i(im)=1−(1−1)m=1.
证毕.
证明二:
使用数学归纳法,大致思路是构造归纳时利用 n=2 时的结论进行拆分.具体证明过程较为冗长,这里略过.
二项式反演
记 fn 表示恰好使用 n 个不同元素形成的特定结构的方案数,gn 表示从 n 个不同元素中选出 i 个元素形成的特定结构的总方案数.
已知 fi 求 gn 可以直接枚举选出了多少个元素
gn=i=0∑n(in)fi.
而已知 gi 求 fn 的过程就叫做二项式反演,其式子是:
fn=i=0∑n(in)(−1)n−igi.
以下是证明过程:
展开 gi,交换求和顺序:
fn=i=0∑n(in)(−1)n−i[j=0∑i(ji)fj]=i=0∑nj=0∑i(in)(ji)(−1)n−ifj.=j=0∑ni=j∑n(in)(ji)(−1)n−ifj=j=0∑nfji=j∑n(in)(ji)(−1)n−i.
由 (5) 式:
fn=j=0∑nfji=j∑n(jn)(n−jn−i)(−1)n−i=j=0∑n(jn)fji=j∑n(n−jn−i)(−1)n−i
令 k=i−j,则 i=k+j,代入:
fn=j=0∑n(jn)fjk=0∑n−j(kn−j)(−1)n−j−k.
由 (9) 式:
fn=j=0∑n(jn)fj[n=j]=fn.
证毕.
这个式子的组合意义还是不够好.事实上,更常用的式子是:
若
gi=j=i∑n(ij)fj,
则有
fi=j=i∑n(ij)(−1)j−igj.
这个式子的组合意义非常重要:现有 n 个不同的元素,fi 表示从 n 个元素中恰好选 i 个元素的方案数,gi 表示从 n 个元素中选出 i 个,规定必须选择这 i 个元素,而其他 n−i 个元素不做限制(一种说法称之为“钦定”).简单地说,就是恰好和至少之间的转换.即容斥原理的推广.
对于前一个式子,即从“恰好”到“至少”,对于一个“恰好”有 j 个元素的方案,我们可以任选其中 i 个作为我们钦定的元素,这就是系数 (ij) 的由来.
对于后一个式子,即从“至少”到“恰好”,对于 gi,它本身包含了所有恰好 i 个元素的方案,但是同时也包含了恰好 i+1,i+2,⋯ 个元素的方案,所以我们利用容斥的思想将多算的方案去除.这就是系数 (−1)j−i 的由来.
错位排列
错位排列是没有任何元素出现在其有序位置的排列.对于 1∼n 的排列 p,如果满足 pi=i,则称 p 是错位排列.
n 个元素的错排数记作 Dn,对于 Dn 的计算,常见的有两种方法:
方法一:
考虑从 n−1 个元素的情况递推到 n 个元素的情况,我们暂时把第 n 个元素放在第 n 个位置:
-
如果前 n−1 个元素已经构成错排,那么将第 n 个元素与前 n−1 个元素的任意一个交换位置即可生成长度为 n 的错排,共 (n−1)Dn−1 种方案.
-
如果前 n−1 个元素中一个元素满足 pi=i 而其他元素不满足,则令第 n 个元素与 i 交换位置即可,共 (n−1)Dn−2 种方案.
其他情况不可能通过一次操作将原排列变为错排.
所以有
Dn=(n−1)(Dn−1+Dn−2).
方法二:
考虑容斥.存在至少 k 个位置满足 pi=i 的方案数为 (kn)(n−k)!,由二项式反演:
Dn=i=0∑n(−1)i(in)(n−i)!=n!i=0∑ni!(−1)i.
当 n→∞ 时,对于一个随机的排列,形成错位排列的概率为:
P=n→∞limn!Dn=e1≈36.8%.
圆排列
n 个不同元素中选出 r 个元素排列成一个圆的形状就叫做圆排列.圆排列的排列数记作 Qnr.
对于任意一个圆排列,其从任意位置断开都可以唯一对应一个线性排列,所以有
Qnr=rAnr=r⋅(n−r)!n!.
卡特兰数
卡特兰数是计数问题中常用的一个数列,记作 Cn.Cn 的值可以用以下这些问题描述:
-
n 对括号组成的合法括号序列数.
-
n 个 1 和 n 个 −1 组成的数列中,满足任意位置前缀和大于等于 0 的数列数目.
-
一个栈的入栈序列为 1,2,3,⋅,n,合法出栈序列数.
可以证明以上这些问题等价.
由组合意义不难得到
Cn={1,∑i=0n−1CiCn−i−1,n=0,n≥1.
但是这个式子较为复杂.接下来将给出更为简洁的计算方法.
事实上,有
Cn=(n2n)−(n−12n).
以下是证明过程:
引理:方格图中,每次只能向右或向上走.从 (0,0)(左下角)走到 (n,m)(右上角)的方案数为
(nn+m)=(mn+m).
证明考虑总步数一定为 n+m,其中选出 n 步向右,剩余 m 步向上.
Cn 可以表述为:方格图中,每次只能向右或向上走.从 (0,0) 走到 (n,n) 且路径不经过直线 y=x 上方(但可以触碰).
从 (0,0) 走到 (n,n) 的总方案数为 (n2n).那么只需计算出不合法的方案数即可.
显然所有不合法的方案都至少触碰过一次直线 y=x+1.
对于所有不合法的路径,我们在其第一次触碰到直线 y=x+1 后的每一步都沿直线 y=x+1 对称,即向右变为向上,向上变为向右,那么原本的终点 (n,n) 就变为了 (n−1,n+1),这样,每一条能到达 (n−1,n+1) 的路径都可以与一条能到达 (n,n) 且不合法的路径一一对应,那么不合法的方案数就是 (n−12n).
这个方法叫做反射容斥.
下图展示了 n=6 时的一个情况,其中橘色直线是 y=x,黄色直线是 y=x+1,红色路径是一条合法路径,绿色路径是一条不合法路径,蓝色路径是绿色路径的“反射”.
第二类斯特林数
第二类斯特林数(斯特林子集数){nm}(也记作 S(n,m)),表示将 n 个不同的元素划分为 m 个互不区分的非空子集的方案数.
由组合意义可得递推式:
{nm}={n−1m−1}+m{n−1m}
边界为 {n0}=[n=0].
当我们加入一个新元素时:
第二类斯特林数通项公式:
{nm}=i=0∑mi!(m−i)!(−1)m−iin.
证明:
考虑容斥.设将 n 个不同元素划分到 i 个集合(允许存在空集)的方案数为 gi,将 n 个不同元素划分到 i 个不同的集合(不允许存在空集)的方案数为 fi.
则有
gi=in=j=0∑i(ji)fj.
由二项式反演
fi=j=0∑i(−1)i−j(ji)gj=j=0∑i(−1)i−j(ji)jn=j=0∑ij!(i−j)!i!(−1)i−jjn.
由于第二类斯特林数要求集合间互不区分,所以
{nm}=m!fm=i=0∑mi!(m−i)!(−1)m−iin.
第一类斯特林数
第一类斯特林数(斯特林轮换数)[nm](也记作 s(n,m)),表示将 n 个不同的元素划分为 m 个互不区分的非空轮换(即圆排列)的方案数.
由组合意义可得递推式:
[nm]=[n−1m−1]+(n−1)[n−1m].
边界为 [n0]=[n=0].
当我们加入一个新元素时:
第一类斯特林数没有实用的通项公式.
分拆数
将 n 拆分成 m 个可重复且不考虑顺序的正整数之和,其方案数即为分拆数,记作 p(n,m).如果不限制拆分份数,其方案数记作 p(n).
简单的做法是使用动态规划递推:
法一:
设 fi,j 为将 i 拆分,且使用的最大数不大于 j 的方案数,转移:
fi,j←fi,j−1+fi−j,j.
边界条件 f0,0=1.
事实上是一个完全背包的形式.只能计算 p(i),但是空间复杂度可以优化至一维.
法二:
设 fi,j 表示将整数 i 拆分为 j 个正整数之和的方案数.转移:
fi,j←fi−j,j+fi−1,j−1.
边界条件是 f0,0=1.
可以这样理解:
对于一种合法方案,其拆分出的数降序排序后可以一一对应一个序列,则我们只需求出这样的序列数.
生成这样的序列 fi,j 可以通过:
-
将原序列中的所有数加 1,共 fi−j,j 种方案;
-
在原序列末尾插入一个 1,共 fi−1,j−1 种方案.
这样就可以计算 p(n,m) 了.
球盒模型
求 n 个球放入 m 个盒子的方案数问题的各种版本:
| 球是否相同 |
盒子是否相同 |
允许空盒 |
计算公式 |
| 不同 |
不同 |
允许 |
mn |
| 不同 |
不同 |
不允许 |
m!⋅{nm} |
| 相同 |
不同 |
不允许 |
(m−1n−1) |
| 相同 |
不同 |
允许 |
(m−1n+m−1) |
| 不同 |
相同 |
不允许 |
{nm} |
| 不同 |
相同 |
允许 |
i=1∑m{ni} |
| 相同 |
相同 |
不允许 |
p(n,m) |
| 相同 |
相同 |
允许 |
p(n+m,m) |
证明在上文中大都有所涉及,这里不做赘述.
常用模板
O(n2) 计算组合数:
1 2 3 4 5 6 7 8
| int n,C[N][N]; void init(){ for(int i=0;i<=n;i++){ C[i][0]=1; for(int j=1;j<=i;j++) C[i][j]=C[i-1][j]+C[i-1][j-1]; } }
|
O(nlogV) 预处理阶乘和逆元,O(1) 计算组合数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| typedef long long ll; constexpr int mod=998'244'353; int n,fact[N],inv[N]; ll qpow(ll a,int b=mod-2){ ll res=1; while(b){ if(b&1) res=res*a%mod; a=a*a%mod; b>>=1; } return res; } void init(){ fact[0]=1; for(int i=1;i<=n;i++) fact[i]=ll(fact[i-1])*i%mod; inv[n]=qpow(fact[n]); for(int i=n-1;i>=0;i--) inv[i]=ll(inv[i+1])*(i+1)%mod; } ll C(int n,int m){ return ll(fact[n])*inv[m]%mod*inv[n-m]%mod; }
|
习题
参考资料
普通高中教科书 数学(A 版) 选择性必修 第三册;
OI Wiki.