组合数学(未完结)

组合数学

组合数学应该是 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\) 个元素排成一列,产生的不同排列的数量为:

\[\mathrm A_n^m=\frac {n!}{(n-m)!}=n\times (n-1) \times \cdots \times (n-m+1) \]

组合数

\(n\) 个不同元素中取出 \(m\) 个组成一个集合(不考虑顺序),产生的不同集合数量为:

\[\mathrm C_n^m=\frac {n!}{m!(n-m)!}=\frac {n\times (n-1) \times (n-2)\times \cdots \times (n-m+1)}{m \times (m-1) \times \cdots \times 2 \times 1} \]

基本性质

  1. \(\mathrm C_n^m=\mathrm C_n^{n-m}\)

    展开组合数公式即可。

  2. \(\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\) 种。

    然后加法原理即可。

    证毕。

    这条性质同样也可以看做是杨辉三角

  3. \(\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\)

    二者相等。

    证毕。

二项式定理

\[(a+b)^n=\sum \limits_{k=0}^n \mathrm C_n^k a^k b^{n-k} \]

证明:

数学归纳法。

\(n=1\) 时,\((a+b)^1=\mathrm C_n^0a^0b^1+C_n^1a^1b^0=a+b\) 成立。

假设当 \(n=m\) 时命题成立,当 \(n=m+1\) 时:

\[\begin{aligned}(a+b)^{m+1} & =(a+b)(a+b)^m\\&=(a+b)\sum \limits_{k=0}^m \mathrm C_m^k a^k b^{m-k}\\&=\sum \limits_{k=0}^m \mathrm C_m^k a^{k+1}b^{m-k}+\sum \limits_{k=0}^m \mathrm C_m^k a^k b^{m-k+1}\\&=\sum \limits_{k=1}^{m+1}\mathrm C_m^{k-1} a^k b^{m-k+1}+\sum \limits_{k=0}^m \mathrm C_m^k a^k b^{m-k+1}\\&=\sum \limits_{k=0}^{m+1}(\mathrm C_m^{k-1}+\mathrm C_m^k)a^k b^{m-k+1}\\&=\sum \limits_{k=0}^{m+1} C_{m+1}^k a^k b^{m+1-k}\end{aligned} \]

证毕。

组合数求和

  • 对角线求和\(\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}\)

组合数求法

  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;
    }
    
  2. 乘法逆元

    此方法主要用于结果要求模大素数时。比如我们要求 \(\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;}
    
  3. 质因数分解

    因为 \(\mathrm C_n^m=\frac {n!}{m!(n-m)!}\),所以可以对分子,分母快速分解质因数,在数组中保存各项质因子的质数。然后把分子,分母各个质因子的指数对应相减(把分母消去),最后把剩余质因子乘起来即可。

    时间复杂度:\(O(n \log n)\)

Lucas 定理

\(p\) 是质数,则对于任意整数 \(1 \le m \le n\),有:

\[C_n^m \equiv C_{n \bmod p}^{m \bmod p} \times C_{n/p}^{m/p} \pmod p \]

也就是把 \(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\}\) 的全排列个数为:

\[\frac {(r+k-1)!}{r!(k-1)!}=\mathrm C_{k+r-1}^r=\mathrm C_{k+r-1}^{k-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\) 个元素,这时有两种情况:

    1. 把它放到位置 \(n\),那么对于除 \(n\) 以外的 \(n-1\) 个元素,由于第 \(k\) 个元素放到了位置 \(n\),所以考虑剩下 \(n-2\)个元素的错排即可,有 \(D_{n-2}\) 种放法;

    2. \(k\) 个元素不放到位置 \(n\),这时对于这 \(n-1\) 个元素的错排,有 \(D_{n-1}\) 种放法。

根据乘法原理加法原理可知:

\[D_n=(n-1)(D_{n-1}+D_{n-2}) \]

另外:

\[D_n=A_n^n-A_n^{n-1}+A_n^{n-2}-A_n^{n-3}+\cdots+(-1)^n A_n^0=\sum \limits_{i=0}^n (-1)^i\times A_n^{n-i} \]

这一点是根据容斥原理推出来的。

也就是说,\(n\) 的错排等于 \(n\) 的全排列,减去至少有一个数不是错排的方案数,加上至少有两个数不是错排的方案数,减去至少有三个数不是错排的方案数……

注意,容斥原理是总方案数-不合法方案数,这里的“不是错排”就是“错排”的反义,所以不是错排的方案数就是不合法方案数。

错位排列前几项:\(0,1,2,9,44,265\)

圆排列

\(n\) 个人全部来围成一圈,所有的排列数记为 \(Q_n^n\)

考虑其中已经排好的一圈,从不同位置断开,变成不同的队列。所以有:

\[Q_n^n \times n=A_n^n \]

那么部分圆排列的方案数(即 \(n\) 个人中挑出 \(r\) 个人围成一圈)为:

\[Q_n^r=\frac {A_n^r} {r}=\frac {n!}{r \times (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 种情况:

  1. 球异,非空

    • 盒同

      实际上就是第二类斯特林数,因此答案是 \(S(n,m)\)

      第二类斯特林数的求法下面会提到,这里不做赘述。

    • 盒异

      就相当于是计算出上述盒同的所有情况后给盒子编号,所以需乘上盒子的全排列。

      所以答案是 \(m!\times S(n,m)\)

  2. 球异,可空

    • 盒异

      每个球都有 \(m\) 种放法,那么 \(n\) 个球就是 \(n\)\(m\) 相乘,也就是 \(m^n\) 种放法。

    • 盒同

      和 1 中的第一种情况的区别在于,盒子可以空放了。

      那么枚举空盒个数累计到第二类斯特林数即可。因此,答案为 \(\sum \limits_{k=1}^m S(n,k)\)

  3. 球同,非空

    • 盒异

      隔板法。题目相当于把 \(n\) 个球分成 \(m\) 段,那么需要 \(m-1\) 个隔板。

      \(n\) 个球之间有 \(n-1\) 个缝隙。所以问题就转化为从 \(n-1\) 个缝隙中找到 \(m-1\) 个插进去。

      所以答案为 \(\mathrm C_{n-1}^{m-1}\)

    • 盒同

      请先移步到 4 第二种情况。在那一种情况基础之上,当遇到非空,我们强制给每个盒子一个球转化为可空,方案数就为 \(dp_{n-m}{m}\)

  4. 球同,可空

    • 盒异

      同样和 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\) 个盒子中是否有空盒

      1. 有空盒:新增一个空盒,贡献 \(dp_{i,j-1}\)

      2. 无空盒:向每个盒子放入一个球使其非空,贡献 \(dp_{i-j,j}\)

两类特殊的数

卡特兰数

给定 \(n\) 个 0 和 \(n\) 个 1,它们按照某种顺序排成长度为 \(2n\) 的序列, 满足任意前缀中 0
的个数都不少于 1 的个数的序列的数量为:

\[Cat_n=\frac {\mathrm C_{2n}^n}{n+1}=\mathrm C_{2n}^n-\mathrm C_{2n}^{n-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 多的序列。

因此,以下两种序列构成了一个双射

  1. \(n\) 个 0 和 \(n\) 个 1 排成的,存在一个前缀 0 比 1 多的序列;

  2. \(n-1\) 个 0 和 \(n+1\) 个 1 排成的序列。

根据组合数的定义,后者显然有 \(\mathrm C_{2n}^{n-1}\) 个。

综上,由 \(n\) 个 0 和 \(n\) 个 1 排成的,任意前缀中 0 的个数都不少于 1 的个数的序列的数量为:

\[Cat_n=\mathrm C_{2n}^n-\mathrm C_{2n}^{n-1}=\frac {\mathrm C_{2n}^n}{n+1} \]

证毕。

推论

  1. \(n\) 个左括号和 \(n\) 个右括号组成的合法括号序列的数量为 \(Cat_n\)

  2. \(1-n\)\(n\) 个自然数依次进栈,形成的合法的出栈序列的数量为 \(Cat_n\)

  3. \(n\) 个节点构成的不同二叉树的数量为 \(Cat_n\)

  4. \(n+2\) 边形过顶点切分成 \(n\) 个三角形的不同方案数;

  5. 在平面直角坐标系上,每一步只能向上或向右走,从 \((0,0)\) 走到 \((n,n)\) 并且除两个端点之外不接触直线 \(y=x\) 的路线数量是 \(2Cat_{n-1}\)

  6. 给定 \(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……

posted @ 2022-04-19 23:47  向日葵Reta  阅读(215)  评论(3编辑  收藏  举报