多项式学习笔记(1)

· · 算法·理论

前情提要。

本文又名《李白天 信息学竞赛中的生成函数计算理论框架》[^5]学习笔记(但行文结构可能不太一样,而且会加一些里面没讲的东西)。理解可能会较浅显,欢迎交流批评。

因为一次写完不可能(其实是因为我是懒狗),所以我把文章拆成了多篇,这是第一篇,主要讲解一些基础理论。(第二篇绝赞咕咕咕中!!!)

默认在 \mathbb{F}_{\mathsf{NTT}} 域中做运算,也即模数为形如 m\times 2^k+1 的质数时的模域。

感谢 Gemini 老师解答了我的很多疑惑。

概述

使用生成函数解决问题时,我们关心的无非两点:

描述问题的方式被称为符号化方法,本文作为第一篇并不会介绍这一部分的理论。

而提取时,难点会出现在以下对象:

本文期望能对上述问题与工具给出一个梳理性的介绍。

这篇文章会讲什么?

  1. 前置知识:大概地过一下多项式的基础定义与算法;
  2. 在线算法:讲解在线问题、(半)在线卷积与一般的在线算法设计思路;
  3. 拉格朗日反演;
  4. 牛顿迭代;
  5. 远处求值:讲解线性递推与整式递推的远处求值;
  6. 再谈远处求值:
    1. 线性递推:讲解有理函数重建与 Half-GCD 算法;
    2. 整式递推:讲解 D-Finite 相关理论。

1 前置知识

\mathbb{A} 为一个特征为 0 的域。

容易定义其上的加法与乘法,并验证由 \mathbb{A} 上的所有多项式所形成的代数结构是交换环。我们记其为多项式环 \mathbb{A}[z]

值得一提的是本文介绍的大部分工具都不会强依赖于 FFT,也就是说就算你只会暴力卷积,你也可以简单地把下文中的 \mathsf{M}(n) 替换为 \Theta(n^2) 来得到一系列基于暴力卷积的多项式算法(不过直接这么做可能会多 \log,建议还是手推一下)。

通过令多项式的度数取到无穷大,我们得到形式幂级数:

容易验证形式幂级数仍形成一个环(因为只有 [z^0]F 可逆的幂级数可以求逆,所以还不是域),我们将其记作形式幂级数环 \mathbb{A}[[z]]

这里讨论一下稍微复杂一点的复合与复合逆。

首先可以发现的是复合与复合逆的合法性并不那么显然了,为了避免讨论 \mathbb{A} 上的极限,我们在这里只规定两种复合是合法的:F 只有有限项非零,或是 [z^0]G=0

并且我们只讨论 [z^0]F=0\land [z^1]F\ne 0F 的复合逆,此时可以证明复合逆存在且唯一,证明思路是我们可以直接递推出来。

接下来有两种方法能将多项式扩张成一个域,一种方法是直接将对象记作两多项式之比,这给出有理函数:

容易验证有理函数形成一个域,我们记其为分式域 \mathbb{A}(z)

另一种方法是添加负次项,这给出形式 Laurent 级数:

容易验证这给出了形式 Laurent 级数域 \mathbb{A}((z))。并且形式 Laurent 级数上复合的收敛性与形式幂级数一致。

假定读者知道一些基础的生成函数知识。

2 什么是在线算法?

在离线算法中,假设我们想计算 G(F(z))\bmod z^{n},则必须一次性给出 F(z)\bmod z^n。然而在幂级数方程中输入可能由输出决定,这给出在线算法。

这就给了我们一种解决问题的通用方法:

正如多项式算法的基础是卷积,在线算法的基础自然是在线卷积。我们先考虑单输入的半在线卷积:

但事实上 OI 里较为 pratical 的半在线卷积是 \Theta(\frac{n\log^2 n}{\log\log n}) 的。它基于一个 \Theta(n\log^2 n) 的算法。其流程为:

  1. f_{0\dots\frac{n}{2}-1}g_{0\dots\frac{n}{2}-1} 做半在线卷积;
  2. f_{0\dots\frac{n}{2}-1}g_{\frac{n}{2}\dots n-1} 做离线卷积;
  3. f_{\frac{n}{2}\dots n-1}g_{0\dots\frac{n}{2}-1} 做半在线卷积。
  4. f_{\frac{n}{2}\dots n-1}g_{\frac{n}{2}\dots n-1} 做离线卷积。

如图所示[^2]:

该算法用时为 \mathsf{S}(n)=2\mathsf{S}(\frac{n}{2})+2\mathsf{M}(\frac{n}{2}),根据主定理有 \mathsf{S}(n)=\Theta(n\log^2 n)

而我们可以将其改为多叉的,将 fgb 为块长分块,则有这样的算法流程:

  1. i0\frac{n}{b}-1,执行:
    1. f_{ib\dots (i+1)b-1}g_{0\dots b-1} 做半在线卷积;
    2. f_{ib\dots (i+1)b-1}g_{b\dots 2b-1}g_{2b\dots 3b-1}、……各做一次离线卷积。

那么我们就只需要做 2\cdot\frac{n}{b}-1 次规模为 2b 的 DFT,随后是 (\frac{n}{b})^2 次规模为 b 的点乘,最后我们不需要做 (\frac{n}{b})^2 次规模为 b 的 IDFT,因为我们可以把贡献到的下标范围相同的点值加起来再 IDFT 回去,这样只要做 2\cdot\frac{n}{b}-2 次规模为 2b 的 IDFT。

于是该算法用时为 \mathsf{S}(n)=\frac{n}{b}\mathsf{S}(b)+\Theta(n\log n+\frac{n^2}{b}),取 b=\frac{n}{\log n} 可得 \mathsf{S}(n)=\Theta(\frac{n\log^2 n}{\log\log n})

而可以证明的是,半在线卷积和在线卷积是等难的。

以在线卷积为基础,根据引理 2.3,我们就可以得到一系列多项式初等函数的在线算法了。

同时在线算法还能用来解幂级数方程,我们以一道例题来讲解其过程。

分析常数项可以发现 [z^0]F(z)=0,而在知道 [z^n]F(z) 后,我们可以更新 \displaystyle [z^n]G(z)=[z^n]\sum_{i\ge 1}\frac{F(z^i)}{i},进而更新 [z^n]\exp G(z),从而在乘上 A(z) 后更新 [z^{n+1}]F(z)。这是一个在线的过程,将刚才提到的算法都替换为对应的在线算法即可。

3 什么是拉格朗日反演?

借助拉格朗日反演,我们得以建立原函数与其复合逆系数的关系。而这基于形式 Laurent 级数上特殊的形式留数,它的特殊之处在于求导,因为 z^0 求导后系数不会传递给 z^{-1}

事实上观察上述证明过程可以发现这一引理完全可以扩展到 \operatorname{ord}F\ne 1 的情况,此时有 \operatorname{res}F'(z)F^k(z)=[k=-1]\operatorname{ord}F,不过和接下来要干的事情没啥关系,所以不管了。

到这里你大概能猜出来我们接下来要做什么了:多项式复合能同时对不同的 k 提供 F^k(z),那么借助引理 3.2,我们只需要乘上 F'(z) 再取 [z^{-1}] 就能筛选出满足 [k=-1] 的那一项,从而做到定向提取系数。

根据线性性,我们还可以得到拉格朗日反演的扩展版本:

通过修改证明过程,我们还能得到拉格朗日反演的变体:

它同样也有其扩展版本:

这里应该有一些例题,但是我咕了。

4 什么是牛顿迭代?

我们熟知实数域上的泰勒展开:

f(z)=\sum_{i\ge 0}\frac{f^{(i)}(z_0)}{i!}(z-z_0)^i

借助泰勒展开,我们得到了一种在方程求解领域相当强大的工具:牛顿迭代。我们自然会希望得到它在幂级数环上的自然扩展。

一般的幂级数方程描述起来相当麻烦,因此我们这里只考虑一类叫做代数方程的幂级数方程。

(这里的 G(t) 也能看成一个二元幂级数 G(z,t)。于是我们可能会混用一些 G(z,t)G(t),请读者自行鉴别。)

而我们可以证明,实数上的泰勒展开在 \mathbb{A}[[z]][[t]] 上仍然成立。

猜测牛顿迭代也是成立的。

和在线卷积相比,牛顿迭代在方程求解的时间复杂度上更胜一筹。这是因为从 y_n 推到 y_{n+1} 只需要在 {\!}\bmod z^{2^{n+1}} 意义下运算,因此计算 y\bmod z^{N} 的用时会满足 T(N)=T(\frac{N}{2})+R(N),而绝大多数情况下 R(N)=\Omega(N),所以有 T(N)=\Theta(R(N))

但是牛顿迭代的常数可能会更差,这是因为计算 \delta_n 时我们可能会调用其他过程,比如另一次牛顿迭代,而常数便会随之翻倍。一些精细实现可以大幅优化其常数,本文不提。

我们简单地提一下常见函数怎么使用牛顿迭代计算:

而只要对牛顿迭代做一些扩展,我们还能处理更复杂的一类方程,比如含有下标偏移的方程:

我们在第二章里给出了一个基于在线算法的做法,现在我们希望给出一个基于牛顿迭代的做法。

这个方程不是代数方程,因为其中出现了下标偏移 F(z^i)。但是重新考察序列 \{y_i\} 的含义,实质上是逐步求出 y_n\equiv F\pmod{z^{2^n}}。而我们在计算 y_{n+1} 时可以在 {\!}\bmod z^{2^{n+1}} 意义下计算原方程,并且对任意 k\ge 2F(z^k)\equiv y_n(z^k)\pmod {z^{2^{n+1}}},所以我们在递推时总是可以把 F(z^i) 当作常数!

这样一来方程就被重新书写为 F\equiv A\exp\left(F+B\right)\pmod{z^{2^{n+1}}},这就是一个普通的代数方程了,后续步骤略。

5 什么是远处求值?

有时,当我们提取某一幂级数 F(z) 或数列 \lang f_i\rangn 次项系数时,希望得到一个 o(n) 的做法,这被称为远处求值问题,目前较成熟的两类远处求值题型分别为线性递推与整式递推。

线性递推

该算法分为两部分。首先,我们会希望把线性递推转化为有理函数。

这样一来,我们就把线性递推数列的远处求值问题转化成了有理函数 \dfrac{P(z)}{Q(z)} 的远处求值问题。这一引理同时还指出,这一转化可以做到 \Theta(\mathsf{M}(k)),不会成为瓶颈。

虽说 Bostan-Mori 算法的常数优于 Fiduccia 算法,但 Fiduccia 算法也是有其优势的:其算法核心是先计算一个形如 z^n\bmod \Gamma(z) 的式子,再计算一个形如 \lang z^n\bmod \Gamma(z),v_0\rang 的内积。而这里的 \Gamma(z) 只和 a 的线性递推式 b_1,\dots,b_k 有关,v_0 只和 a 的初值 a_0,\dots,a_{k-1} 有关。这允许我们对同一线性递推式预处理出 z^n\bmod\Gamma(z),以此 \Theta(k) 地计算单组初值所给出的 a_n

我们希望对 Bostan-Mori 做类似的改造。定义 \displaystyle\mathcal{F}_{n,k}(A)=\sum_{i=0}^{k-1}z^i[z^{n-k+1+i}]A,则对 \deg P<k 的多项式 P,就有 [z^n]\frac{P(z)}{Q(z)}=[z^{k-1}]P(z)\mathcal{F}_{n,k}(\frac{1}{Q(z)})

对多组不同初值提取系数的一个特例是单组初值提取一段区间内的系数。

接下来我们稍微加强一下问题。

整式递推

6 你说得对,但是什么是远处求值?

上一章中,我们讨论了线性递推与整式递推提取远处系数的方法,但求出递推式本身也是一个难题,本章将着重探讨这方面的内容,并揭示两种递推背后更深层的结构。

线性递推与有理函数重建

碎碎念:在撰写本节时,作者不明不白地跑去学了一下多项式上的逼近理论,然后发现其实并不需要,,,你可以在这里看到被我删掉的这部分内容。

根据引理 5.1,任意线性递推的结构都可以由有理函数 \frac{P(z)}{Q(z)} 刻画。我们自然会好奇一个问题(这被称为有理函数重建问题):如果给定数列的前若干项 \displaystyle A(z)=\sum_{i=0}^{n-1} a_iz^i,能不能求出一个 \deg P(z)<\deg Q(z)\deg Q(z) 最小的有理函数 \frac{P(z)}{Q(z)},使得 A(z)\equiv\frac{P(z)}{Q(z)}\pmod{z^n}

我们先将问题适当泛化:

那么还原线性递推式相当于在找到 M=z^nA 的一组有理逼近(d_Pd_Q 的取值我们先按下不表),要求 \deg P<\deg Q\deg Q 尽量小。

我们可以把这一同余方程改写成 P=QF+RM,这启示我们将其刻画为 Euclid 过程:

  1. R_{-1}=MR_{0}=F
  2. 对所有 i\ge 0,令:
  3. 重复上述过程,直到首次出现 R_i=0

容易在这一过程中同时维护 S_iT_i,使得对任意 i 都有 R_i=S_iF+T_iM,它们满足与渐近分数 \frac{P_i}{Q_i} 类似的递推关系:

  1. 对所有 i\ge 0S_{i+1}=S_{i-1}-A_iS_{i}T_{i+1}=T_{i-1}-A_iT_{i}

经过一些观察,可以发现:

R_i=S_iF+T_iMP=QF+RM 中,左式的 R_iS_i 扮演的角色与右式的 PQ 一样,所以实际上我们可以直接根据这一点来找有理逼近。

定理 6.1 与定理 6.2 实际上保证了 d_P+d_Q=\deg M-1 的有理逼近是唯一存在的,所以我们可以只关心这些解。而回到线性递推,我们还要求 \deg P<\deg Q\deg Q 尽量小,而定理 6.1 实际上保证了每一组 (R_i,S_i) 都是 d_P+d_Q=\deg M-1 时的一组有理逼近(且是唯一一组),找到第一组满足 \deg R_i<\deg S_i 的即可。

上半部分中我们将有理函数重建问题完全解决,而实现上我们会需要支持快速对一组 (F,M) 运行 Euclid 过程(下面设 n=\deg M)。因为计算 \lfloor\frac{F}{G}\rfloorF\bmod G 可以在 \Theta((\deg F-\deg G)\deg G) 的时间内完成,因此朴素地进行模拟就能做到 \Theta(\sum (\deg R_{i-1}-\deg R_i)\deg R_i)=\Theta(n^2) 的时间。部分场景已足够使用,不过我们确实能在给力一点,因为有 Half-GCD 算法:

gcd 就是 R 的最后一项非零项,于是我们希望找到一种方法快速求出 R 的值。可以发现 R 上的递推关系可以使用矩阵刻画为 \begin{bmatrix}R_i\\ R_{i+1}\end{bmatrix}=\begin{bmatrix}0&1\\ 1&-Q_i\end{bmatrix}\begin{bmatrix}R_{i-1}\\ R_i\end{bmatrix},此处 Q_i=\lfloor\frac{R_{i-1}}{R_i}\rfloor。我们记 \lang Q\rang=\begin{bmatrix}0&1\\ 1&-Q\end{bmatrix},则 \begin{bmatrix}R_k\\ R_{k+1}\end{bmatrix}=\lang Q_k\rang\cdots\lang Q_0\rang\begin{bmatrix}R_{-1}\\ R_0\end{bmatrix}

而我们知道 \begin{bmatrix}R_{-1}\\ R_0\end{bmatrix}=\begin{bmatrix}A\\ B\end{bmatrix},现在问题转化为求出 \lang Q_k\rang\cdots\lang Q_0\rang,这里的 Q_i 依赖于 \lang Q_{i-1}\rang\cdots\lang Q_0\rang,所以我们的算法应该类似于一个在线算法。

在线算法的流程通常都和分治有关,但这里朴素算法的用时瓶颈在于矩阵系数的度数可能过大,并且我们还不知道 k 的具体值,所以基于下标的分治方式并不可行,我们需要从度数切入去进行分治。

假设我们已经实现了这样一个叫做 \operatorname{hgcd}(A,B) 的算法,它输入两个多项式 A,B 满足 \deg A>\deg B\ge 0,并输出 AB 之间的 Euclid 过程中的一步 \mathbf{M}=\lang Q_k\rang\cdots\lang Q_0\rang,使得令 \begin{bmatrix}C\\ D\end{bmatrix}=\mathbf{M}\begin{bmatrix}A\\ B\end{bmatrix} 后有 \deg C\ge\lceil\frac{\deg A}{2}\rceil>\deg D。显然 \operatorname{hgcd} 的输出是唯一的。

那么在计算 \gcd(A,B) 时,我们只要再求出 Q_{k+1}=\lfloor\frac{C}{D}\rfloor 并将 \begin{bmatrix}C\\ D\end{bmatrix} 左乘上 \lang Q_{k+1}\rang,则新的向量就满足两维的度数均 <\lceil\frac{\deg A}{2}\rceil,也就可以递归下去了。于是接下来我们只需要考虑 \operatorname{hgcd} 的设计。

(不直接使得 \lceil\frac{\deg A}{2}\rceil>\deg C>\deg D 是为了方便 \operatorname{hgcd} 算法自身的设计。)

剖析一下带余除法的结构,设 Q=\lfloor\frac{A}{B}\rfloor,我们发现当 \deg A\ge \deg B 时总有 \deg Q=\deg A-\deg B,而计算 Q\deg Q-i 次项时我们只要知道 A\deg A-i 次项与 B 的最高次项,而为了得到正确的 A[\deg A-\deg Q,\deg A] 次项,我们还要知道 B[\deg B-\deg Q,\deg B] 次项。这就是我们为了计算 Q 需要知道的所有信息了!

这启示我们在进行 Euclid 过程时,前半部分可能不会涉及到 AB 的更低项,于是我们可以分治。基于这一启示,我们就可以设计出 \operatorname{hgcd} 算法的一个大致框架了:

  1. m=\lceil\frac{\deg A}{2}\rceil
  2. \deg B<m,则单位矩阵就满足 \operatorname{hgcd} 的输出要求,直接返回即可;
  3. 否则,我们将 AB 分别按 m 次项为界划分为两个多项式 A_0z^m+A_1B_0z^m+B_1,满足 \deg A_1,\deg B_1<m
  4. 只考虑 A_0B_0,在其上运行 \operatorname{hgcd} 算法,我们猜测这一步对低次项的影响是极其有限的,也就是仍有 \deg A=\deg A_0+m\deg B=\deg B_0+m
  5. 此时 \deg B_0<\frac{\deg A_0}{2}\le\frac{\deg A}{4},于是辗转相除一次后 A'B 的次数都小于 \frac{3}{4}\deg A(忽略一些取整);
  6. 再取 m\frac{\deg A}{4} 重新划分 A'_0,A'_1,B_0,B_1,并重新对 A'_0,B_0 运行 \operatorname{hgcd} 算法;
  7. 此时 \deg B_0<\frac{\deg A'_0}{2}\le\frac{\deg A}{4},于是 \deg B<\frac{\deg A}{2},成功满足了输出要求。

这一算法用时 T(n) 满足递归式 T(n)=2T(\frac{n}{2})+\Theta(\mathsf{M}(n)),也就有 T(n)=\Theta(\mathsf{M}(n)\log n)。接下来我们补全一些算法和正确性证明的细节。

回忆一下,\lang Q\rang=\begin{bmatrix}0&1\\ 1&-Q\end{bmatrix}

确认一下我们要验证的事情:只需要验证算法的第四步中的猜测是对的,那么 A_0B_0 上的 \operatorname{hgcd} 算法所计算的商的次数就始终不会超过 \frac{\deg A}{4},于是也就不会受到低次项的影响,可以直接作用到 AB 上。

补全一下算法细节(框架中唯一一处需要修正的是第八行):

  1. m=\lceil\frac{\deg A}{2}\rceil
  2. \deg B<m,直接返回单位矩阵;
  3. 否则按 m 次项划分 AB。设 \mathbf{R}=\operatorname{hgcd}(A_0,B_0)
  4. \begin{bmatrix}C\\ D\end{bmatrix}=\mathbf{R}\begin{bmatrix}A\\ B\end{bmatrix}
  5. \deg D<m,直接返回 \mathbf{R}
  6. 否则设 Q=\lfloor\frac{C}{D}\rfloorE=C\bmod D
  7. \deg E<m,直接返回 \lang Q\rang\mathbf{R}
  8. 否则设 \bm {k=2m-\deg D},并依此划分 \bm D\bm E
  9. \mathbf{S}=\operatorname{hgcd}(D_0,E_0),返回 \mathbf{S}\lang Q\rang\mathbf{R}

有了强大的 Half-GCD 算法,我们就不难计算下列值了,细节留作习题:

整式递推与 D-Finite

与线性递推不同,整式递推强大之处在于其表达力,以至于相当多的数列都能用整式递推刻画,但另一方面,我们又难以得到整式递推的一个简短的封闭形式。于是我们并不会那么关心怎么用有限项数列“还原”其整式递推式,而是关心怎么从一个完整的表达式来“找到”其整式递推式。

我们针对数列的工具并不多,一个明智的想法是搬到形式幂级数环上工作。

这实际上在说 F 满足某个线性常微分方程。可以发现整式递推与微分有限是一体两面:

值得注意的是 \lang f_i\rang 的初值信息并没有蕴含在 C_i 中,也就是说一组 C_i 可以对应多个 F,需要人为限定初值,这些初值也就是 [z^0]F,[z^0]F',\dots,[z^0]F^{(k-1)}

接下来我们就可以讨论更易操作的微分有限函数了。

为了判定 F 是不是微分有限的,我们将工作分为两部分:一些常见的微分有限函数与其上可以出现的构造方法。

很神奇的是常见的微分有限函数其实只有两种:一种是幂函数 F=z^k,其满足微分方程 kF-zF'=0

而另一种就是超几何函数 \displaystyle F=F\left(\begin{matrix}a_1,\dots,a_m\\ b_1,\dots,b_n\end{matrix}\middle| z\right)=\sum_{i\ge 0}\frac{a_1^{\overline{i}}\dots a_m^{\overline{i}}}{b_1^{\overline{i}}\dots b_n^{\overline{i}}}\frac{z^i}{i!}。不难验证它满足微分方程:

\left(\frac{\mathrm{d}}{\mathrm{d}z}\prod_{i=1}^n (\vartheta+b_i-1)\right)F=\left(\prod_{i=1}^m (\vartheta+a_i)\right)F

证明:左式在求导前等于 \displaystyle\sum_{i\ge 0}\frac{a_1^{\overline{i}}\dots a_m^{\overline{i}}}{b_1^{\overline{i-1}}\dots b_n^{\overline{i-1}}}\frac{z^i}{i!},右式等于 \displaystyle\sum_{i\ge 0}\frac{a_1^{\overline{i+1}}\dots a_m^{\overline{i+1}}}{b_1^{\overline{i}}\dots b_n^{\overline{i}}}\frac{z^i}{i!}

而很多常见幂级数实际上都只是超几何函数的一个特例:

更一般地,所有常数项为 1 且相邻项系数比值为“关于次数的有理函数”的幂级数均为超几何函数,证明略。

接下来我们再看构造方法,为方便验证一种构造方法是否真的能生成微分有限的幂级数,我们有引理:

为什么是 \mathbb{A}(z) 而不是等价的 \mathbb{A}[z](只需要乘上/除以所有分母的 \operatorname{lcm})?这是因为前者是而后者只是,我们自然会希望在性质更好的结构上工作。

也可以用数列的语言写出与其等价的命题。

值得注意的是,你不应该将数列看成一串数 f_0,f_1,\dots,而应该看成一个以下标 n 为自变量的函数 f(n),这样一来引理 6.3' 的表述应该就很自然了。

这就给了我们构造性地得到某个幂级数的 ODE 的能力。当然手算时还有一种可行的方法是直接大力求导并对比系数,可参见我的一篇魔怔文。

不过一个可能更 pratical 的做法是在分析出 F 能整式递推后直接用其他方式求出 F 的前若干项,这样我们就能得到关于 C_i 系数的若干条方程,直接求解即可。因为整式递推式通常都不会很复杂,所以这个做法在用时上是可接受的。

[^1]: Hoeven, J. Faster relaxed multiplication. (HAL,2012), http://hal.archives-ouvertes.fr/hal-00687479

[^2]: Hoeven, J. New algorithms for relaxed multiplication. https://www.texmacs.org/joris/newrelax/newrelax.html#dichrelax-fig

[^3]: Kinoshita, Y. \& Li, B. Power Series Composition in Near-Linear Time. (2024), https://arxiv.org/abs/2404.05177

[^4]: Bostan, A. \& Mori, R. A Simple and Fast Algorithm for Computing the N-th Term of a Linearly Recurrent Sequence. CoRR. abs/2008.08822 (2020), https://arxiv.org/abs/2008.08822

[^5]: 李白天, 信息学竞赛中的生成函数计算理论框架. IOI2021 国家集训队论文集 (2021)