yzl_lec2

date: 25.08.03

课程大纲

(1)从双射法的角度,初步介绍 Catalan 数相关的知识。

(2)复习一下线性筛素数,顺便告知 $\mu$ 的计算方式,现场实现一下。为以后课讲和式变换作铺垫。

(3)二项式定理。上节课提到学校没学过。

(4)顺便讲一些小知识。

映射视角下的计数原理

普通映射视角:分类计数

普通映射视角下,分类计数可以看成是构造了一个 组合对象 到 类别 的映射。 我们要给一个给定的组合对象,规定它的唯一特征,从而符合映射的要求。

这样,计算组合对象的数量,等价于遍历所有类别, 针对一个特定的类别考虑有多少个组合对象指向它。

双射视角:等价转化

双射视角下,两个集合大小相等。 我们需要找到两个集合之间的一一对应。 从而数出一边,就能直接知道另一边。

两种视角可以叠加使用。就像 $a=b$ 和 $b=c$ 能推出 $a=c$ 一样。

例子:数小朋友的数量

有一些小朋友,它们当然有自己的性别。它们各自有一个书包,书包有一种颜色。

第一种思路:我可以考虑 小朋友 到 性别 的映射。 一个小朋友不会有两种性别,所以这的确是一个映射。 我在性别端进行计数。数出每个性别的小朋友的数量,再把这些数量加起来。

第二种思路:小朋友 和 书包 之间是一一对应的关系。它们的总数相同,所以我把书包给数出来即可。

第三种思路:我是不是可以再把两种思路综合一下? 我再考虑书包的分类计数,按照颜色分类,分别数,加起来,从而数出书包总数。

技术补充:一种验证双射的方法

在先前的语境下,这里要补充的技术是:
  • 如果小朋友 $a$ 的书包 $b$ 的主人是小朋友 $a$
  • 如果书包 $b$ 的主人 $a$ 的书包是书包 $b$
  • 那么这就构造出了一个双射。

    原理在于,如果一个单射反向连边后仍为映射,或者说有两个方向的单射,即为双射。

    实际上,这里暗示了辅助映射的存在性,即书包回到主人的映射。
    如果说能找到这样的一个逆映射,实际上就能说明这是一个双射。

    一些大小等于 Catalan 数的集合

    下表列出了一些(大小为 $n$ 的结构)组成的集合, 它们的元素个数为第 $n$ 个 Catalan 数 $C_n$:

    类型示例$C_3$ 特点
    合法括号序列(())()5和合法出入栈序列的对应最自然
    Dyck 路径UUDDUD5计数形式最自然
    二叉树见下方5递归形式最自然

    Dyck 路径计数

    Dyck 路径的定义,是在长度为 $2n$ 的 UD 序列上:

  • 要求有 $n$ 个 U:自然,这些 U 位置确认好后 D 就能确认了。
  • 符合限制:任何前缀,U 的数量不能少于 D 的数量。
  • 分别思考这样的问题:
  • 符合第一个要求的序列有多少个?
  • 符合第一个要求但不符合第二个要求的序列有多少个?
  • 递归形式

    实际上 Catalan 数还满足如下递归式 $$ C_n = \sum\limits_{i=1}^{n} C_{i-1}\cdot C_{n-i} $$

  • 关键在于,每个合法路径,第一位总是 U;
  • 并且总存在第一个归零点,这位总是 D;
  • 上述两个 UD 是配对的。假设第一个归零点配对了 $i$ 组 UD。
  • 它们内部是一个 $i-1$ 的子问题
  • 第一个归零点之后是一个 $n-i$ 的子问题
  • 线性筛复习

    筛欧拉函数,回忆计算公式。

    快速求出 $\varphi(i),i=2,3,4,\cdots,n$。

    其实就是要控制,对于一个素因子 $p$,恰好让它乘一次 $\frac{p-1}{p}$。

    欧拉筛中,合数 $x$,记它的最小素因子是 $p_{\min}$,$x$ 唯一一次被访问(被筛),是在外层循环访问到 $x/p_{\min}$,内层循环循到 $p_{\min}$(由于最小性,之前不会被 break)。

    在欧拉筛的基础上,对于素数,点值是平凡的;对于合数 $x$,由于外层从小到大,$\varphi(x/p_{\min})$ 已知,求 $\varphi(x)$: 如果 $x/p_{\min}$ 不是 $p_{\min}$ 的倍数,考虑给下式子右边扩充一个 $p_{\min}$ 和一个 $1-\frac{1}{p_{\min}}$,乘一下。 $$ \varphi(\frac{x}{p_{\min}}) = \frac{x}{p_{\min}} \cdot \prod_{p|\frac{x}{p_{\min}}} (1-\frac{1}{p}) $$
    反之,如果 $x/p_{\min}$ 是 $p_{\min}$ 的倍数,则求积下标中 $$ p|\frac{x}{p_{\min}} $$ 会枚举到素数 $p_{\min}$。所以只需要补充一个 $p_{\min}$。 顺便一提,欧拉筛也会正好在这之后,结束掉内层循环。

    莫比乌斯函数 $\mu(x)$

    给出 $\mu(x)$ 的计算公式 $$ \mu(x) = \begin{cases} 1 & \text{当 } x = 1 \\ 0 & \text{当 } x \text{ 含有平方因子}\\ &\text{(即存在素数 } p \text{ 满足 } p^2 \mid x) \\ (-1)^k & \text{当 } x \text{ 是 } k \text{ 个不同素数的乘积} \end{cases} $$

    怎么在之前的基础上,设计算法,快速求出 $\mu(i),i=2,3,4,\cdots,n$。

    二项式定理

    $$ (a+b)^n = \sum\limits_{i=0}^{n} \binom{n}{i} a^{i} b^{n-i} $$

    应用:$n$ 个数,二进制枚举子集的复杂度是 $$ \sum\limits_{i=0}^{n}\binom{n}{i} $$ 怎么计算?

    在上面问题的基础上,如果我还要用 $2^i$ 的时间遍历每个大小为 $i$ 的集合 $$ \sum\limits_{i=0}^{n}\binom{n}{i}2^i $$ 怎么计算?

    小知识:C++ 标准

    • C++ 是不断演进的语言,有不同的“标准版本”
    • 标准委员会每几年发布一次新标准,增加新语法、新库等。
    • 常见的有:C++98、C++03、C++11、C++14、C++17、C++20、C++23。
    • 这个数字的意义其实就是发布的年份。

    CCF 的比赛规范

    根据NOI活动的发展形势,NOI科学委员会特对NOI系列活动(包括CSP-J/S在内)中编程语言的使用做如下补充说明:

    1、除题面有明确要求外,C++程序编译默认采用的语言标准为C++14;

    ...

    — 2021年9月1日

    在此之前,只有 2011 年发了一个规范,显然没有跟进当年发布的 C++11。

    IDE(如 Dev-C++)的“一键运行”原理

    • IDE 会在后台调用 g++,自动编译 + 运行
    • 等价于命令:
    • 本质上和命令行一样,只是帮你生成了命令

    如何在命令行指定 C++ 标准

    使用 -std=c++XX 参数,例如:

    常见版本:

    • -std=c++11:支持 lambda、auto
    • -std=c++17:支持结构化绑定
    • -std=c++23:支持 auto 自递归

    C++23 新特性:auto 函数可递归

    在旧版本中,auto 函数不能递归调用自己

    但 C++23 改进了规则,支持如下代码: