多项式,形式幂级数与生成函数学习笔记集合

· · 个人记录

注:从 Typora 复制到洛谷博客后部分公式炸裂,慢慢修吧。

介绍了我所了解的几乎所有有关这个板块的知识内容。

读完这篇文章并不能使你的多项式和计数水平达到一个很高的高度,也许你不能搞清楚为什么 FFT 的本质是循环卷积,幂级数三角函数的组合意义是什么,但至少可以确保你不问出“FFT 做完后为什么只保留实数部分”,“为什么多项式 \ln 之后还是多项式”,“多项式 \exp 这种理论上的东西到底能有什么实际用途”之类的问题。

〇、目录

一、快速傅里叶变换与数论变换

(一)复数与单位根

(二)多项式的表示

(三)快速傅里叶变换

(四)逆离散傅里叶变换

(五)数论变换

(六)任意模数意义下的变换

(七)分治 FFT

(八)高维 FFT

二、一些多项式技巧

(一)秦九韶算法

(二)拉格朗日插值法

(三)多项式除法和取模

(四)多项式快速求值与插值

三、形式幂级数基础

(一)基本运算

(二)泰勒展开

(三)牛顿迭代法

(四)形式幂级数的更多运算

四、生成函数基础

(一)概念

(二)组合意义

一、快速傅里叶变换与数论变换

这一部分将会介绍:复数的基本概念,快速傅里叶变换(FFT),数论变换(NTT)及其部分衍生内容。

(讲复数的基本概念是为了引入单位根,而和原根有关的知识大都和单位根部分的证明相似,且在数论中原根较为熟悉,因此不再单独讲解原根)

(一)复数与单位根

复数,即可以表示成 z=a+bi\ \ (a,b\in \R) 形式的数,其中 a 称为 z实部b 称为 z虚部i 为虚数单位 \sqrt{-1}

复数与平面直角坐标系中的二维向量一一对应,我们将 z=a+bi 对应向量 (a,b),于是平面上的每一个点都代表了唯一的复数,此平面称为复平面,横轴称为实轴,纵轴称为虚轴。

对于 z=a+bi,如果 b=0,则 z 为实数,否则 z 为虚数。如果 a=0b\ne 0,则称 z 为纯虚数。

复数的四则运算:

对于 z_1=a+bi,\ \ z_2=c+di,定义它们的四则运算如下:

  1. 加法:z_1+z_2=(a+c)+(b+d)i,即实部和虚部分别相加,满足交换律和结合律。
  2. 减法:z_1-z_2=(a-c)+(b-d)i,即实部和虚部分别相减。
  3. 乘法:z_1\times z_2=(ac-bd)+(ad+bc)i,可用乘法分配率验证,满足交换律、结合律和对加法的分配率。
  4. 除法:\dfrac{z_1}{z_2}=\dfrac{ac+bd}{c^2+d^2}+\dfrac{bc-ad}{c^2+d^2}i,可通过分母实数化得到,这里 c,d 不同时为 0

对于复数 z=a+bi,定义 \bar z=a-biz 互为共轭复数,满足 z\bar z\in \R

对于复数 z=a+bi,定义 r=\sqrt {a^2+b^2} 为其模长

我们可以用类似极坐标的方法来表示一个复数:z=r(\cos \theta+\sin \theta i),其中 r 为其模长,\theta 为其对应向量与实轴正半轴的夹角,\theta 称为 z辐角

考虑用极式将两复数相乘:

z_1=r_1(\cos \theta_1+\sin \theta_1i),\ \ z_2=r_2(\cos\theta_2+\sin\theta_2i) \\ z_1z_2=r_1r_2((\cos \theta_1\cos \theta_2-\sin\theta_1\sin\theta_2)+(\cos \theta_1\sin\theta_2+\cos\theta_2\sin\theta_1)i)

利用三角函数的和角公式,我们可以得到:

z_1z_2=r_1r_2(\cos(\theta_1+\theta_2)+\sin(\theta_1+\theta_2)i)

于是我们可以总结出:两复数相乘,模长相乘,辐角相加。

接下来考虑方程 x^n=1,它有 n 个不同复数解。

例如,n=3 时,三个解为 x_1=1,\ \ x_{2,3}=\dfrac{1}{2}\pm\dfrac{\sqrt 3}{2}i

将三个解作在复平面上,发现恰好将单位圆三等分。是否对于所有的 n 都有类似性质呢?

x=r(\cos \theta+\sin\theta i) 是方程的解,则 x^n=r^n(\cos(n\theta)+\sin(n\theta)i)=1,这说明:

其中,\theta>0 且最小的一个解是 \theta=\dfrac{2\pi}{n},我们就将这个根 x=\cos(\dfrac{2\pi}{n})+\sin(\dfrac{2\pi}{n})i 称为 \omega_n,其他的根都可以表示为 x=\cos(\dfrac{2k\pi}{n})+\sin(\dfrac{2k\pi}{n})i=\omega_n^k,方程的这些根 x 都称为 n 次单位根

我们还可以将单位根表示成 \omega_n^k=e^{\frac{2\pi ik}{n}} 的形式。

(二)多项式的表示

注意:我们讨论的多项式是广义的,即包含了狭义的多项式与单项式。

我们讲了上面这些,最终希望归到离散傅里叶变换,而在 OI 中这部分的研究主要是在多项式这一对象上的。

多项式的常见表示有两种:系数表示点值表示

  1. 系数表示:即常见的 f(x)=\sum\limits_{i=0}^{n} a_ix^i 的表示方法,n 称为多项式的次数
  2. 点值表示:用 n+1 对点值表示一个 n 次多项式的方法,即给出 (x_0,f(x_0)),\ldots,(x_n,f(x_n))

其中系数表示便于直观地分析多项式的若干性质,以及对多项式进行一些常规的操作。

但是,一旦设及到乘法(称为卷积),系数表示的多项式乘法使用暴力的复杂度为 O(n^2),比较慢。

实际上有一种容易实现的多项式乘法优化,叫做 Karatsuba 乘法,基于每次将一个 n 次的多项式乘法转化成 3\dfrac{n}{2} 次的多项式乘法,根据 Master 定理可分析得到其复杂度为 O(n^{\log_2 3})\approx O(n^{1.585}),但效率依然不够高。

如果我们用点值表示进行卷积,(x_0,f(x_0)),\ldots,(x_n,f(x_n))(x_0,g(x_0)),\ldots,(x_n,g(x_n)) 的卷积就是 (x_0,f(x_0)g(x_0)),\ldots,(x_n,f(x_n)g(x_n)),这个过程是 O(n) 的,且如果 n 等于 f(x)g(x) 的次数之和,那么这样的点值表示可以唯一确定一个 n 次多项式。

这就让我们想到:如果我们能实现一个点值表示和系数表示之间的转换,那么两个系数表示多项式的卷积就可以先转换成点值做卷积以后再转换回来。

(三)快速傅里叶变换

在下面的讨论中,我们假设要操作的多项式为 n-1 次,且 n=2^p\ (p\in \Z^+),如果次数不足只需补至 2 的整数幂次即可。

我们要把多项式 f(x)=\sum\limits_{i=0}^{n-1}a_i\times x^i 转化成其点值表示。

点值表示中的 x_i 是可以任意选取的,我们不妨拓宽数域,选择 \omega_n^k\ (k\in [0,n)) 作为所有的 x_i

之所以选择 \omega_n^k 是因为它有许多好的性质,下面列举一些:

性质 1\omega_{2n}^{2k}=\omega_n^k

证明:\dfrac{2\times 2k\times \pi}{2n}=\dfrac{2k\pi}{n},带入之前写过的式子即得证。

性质 2\omega_n^k=-\omega_n^{k+\frac{n}{2}}

证明:\omega_{n}^{k+\frac{n}{2}}=\omega_n^k\times \omega_{n}^{\frac{n}{2}},而 \omega_{n}^{\frac{n}{2}} 的辐角为 \dfrac{2\times \frac{n}{2}\times \pi}{n}=\pi,所以 \omega_{n}^{\frac{n}{2}}=-1,即得证。

性质 3\omega_{n}^n=1

证明:由定义可得证。

接下来考虑如何利用单位根快速实现多项式不同表示方法的转换。

首先,对于 0\leq k<\dfrac{n}{2},我们分别带入 f(\omega_n^k)f(\omega_n^{k+\frac{n}{2}})

\begin{aligned} & f(\omega_n^k) = \sum\limits_{i=0}^{n-1}a_i\times (\omega_n^k)^i \\ & f(\omega_n^{k+\frac{n}{2}})=\sum\limits_{i=0}^{n-1}a_i\times (\omega_n^{k+\frac{n}{2}})^i \end{aligned}

根据性质 2,我们知道:当 i\equiv 0\bmod 2 时,(\omega_n^{k})^i=(\omega_n^{k+\frac{n}{2}})^i,否则 (\omega_n^{k})^i=-(\omega_n^{k+\frac{n}{2}})^i,于是我们可以用 \omega_n^k 来表示 f(\omega_n^{k+\frac{n}{2}})

f(\omega_n^{k+\frac{n}{2}})=\sum\limits_{i=0}^{n-1}(-1)^i\times a_i\times (\omega_n^{k})^i

i\equiv 0\bmod 2i\equiv 1\bmod 2 的两个部分分开,写成两个部分的和:

f(\omega_n^{k+\frac{n}{2}})=\Big(\sum\limits_{i=0}^{\frac{n}{2}-1}a_{2i}\times (\omega_n^{k})^{2i}\Big)-\Big(\sum\limits_{i=0}^{\frac{n}{2}-1}a_{2i+1}\times (\omega_n^{k})^{2i+1}\Big)

对于 (\omega_n^k)^{2i},可以写成 (\omega_n^{2k})^i,从而根据性质 1 写为 (\omega_{\frac{n}{2}}^k)^i;而对于 (\omega_n^k)^{2i+1},我们提取一个 \omega_n^k 后用类似方法处理,所以可以得到:

f(\omega_n^{k+\frac{n}{2}})=\Big(\sum\limits_{i=0}^{\frac{n}{2}-1}a_{2i}\times (\omega_{\frac{n}{2}}^{k})^{i}\Big)-\omega_n^k\Big(\sum\limits_{i=0}^{\frac{n}{2}-1}a_{2i+1}\times (\omega_{\frac{n}{2}}^{k})^{i}\Big)

为了追求相似的形式,我们对 f(\omega_n^k) 进行类似的变换:

f(\omega_n^{k+\frac{n}{2}})=\Big(\sum\limits_{i=0}^{\frac{n}{2}-1}a_{2i}\times (\omega_{\frac{n}{2}}^{k})^{i}\Big)+\omega_n^k\Big(\sum\limits_{i=0}^{\frac{n}{2}-1}a_{2i+1}\times (\omega_{\frac{n}{2}}^{k})^{i}\Big)

结合以上两式,发现两个括号内的和式是完全一样的,所以我们可以设两个新的多项式:

\begin{aligned} & A(x)=\sum\limits_{i=0}^{\frac{n}{2}-1}a_{2i}x^i \\ & B(x)=\sum\limits_{i=0}^{\frac{n}{2}-1}a_{2i+1}x^i \end{aligned}

于是,f(\omega_n^k)f(\omega_n^{k+\frac{n}{2}}) 的关系可以显式地表达如下:

\begin{aligned} & f(\omega_n^k)=A(\omega_{\frac{n}{2}}^k)+\omega_n^kB(\omega_{\frac{n}{2}}^k) \\ & f(\omega_n^{k+\frac{n}{2}})=A(\omega_{\frac{n}{2}}^k)-\omega_n^kB(\omega_{\frac{n}{2}}^k) \end{aligned}

而我们发现,A(\omega_{\frac{n}{2}}^k),\ B(\omega_{\frac{n}{2}}^k) 这两个 \dfrac{n}{2} 次多项式的点值,本质和求 f(\omega_n^k) 没有区别,只是一个规模一半的子问题,所以我们可以递归求解,递归到 n=1 时直接有 f(x)=a_0

下面来分析这个做法的时间复杂度。

每一个规模为 n 的问题(求所有 f(\omega_n^k))可以拆分成两个规模为 \dfrac{n}{2} 的子问题(求所有 A(\omega_{\frac{n}{2}}^k)B(\omega_{\frac{n}{2}}^k)),然后用 O(n) 的代价合并(每个 f(\omega_n^k) 可以 O(1) 通过两个 A,B 的点值得到),所以整个过程的复杂度为 O(n\log n)(分析过程非常经典,与归并排序的复杂度证明类似)。

上面这个过程叫做离散傅里叶变换(DFT),而这种基于分治的优化即为快速傅里叶变换(FFT)

DFT 与一般傅里叶变换的关系:一般傅里叶变换是积分,DFT 是其离散的版本,积分变为了求和,这个可以稍作了解,毕竟 OI 中基本用不到连续版本的傅里叶变换。

当然,这个递归版本写起来有些麻烦,而且常数较大,所以我们通常使用的是一种非递归版本的 FFT。

观察 FFT 过程中的递归树(图片来源于网络,以 n=16 为例):

其中 \text{Step}\ i 即为第 i 层的递归,写在同一块中的下标即表示同一个多项式的系数。

做完递归以后,我们把下标从左往右写出来(也就是把图中 \text{Step}\ 4 中的数从左到右从上到下写一遍,特别地,最后两个块在图上的位置反了),与其位置对比,可以看出:最终一个位置上的值和这个位置的二进制表示正好是反转的。

简单说明一下:因为每一步只有处于当前多项式偶数次的系数会走到左边,奇数次的系数会走到右边,所以如果 x 的第 i 位为 1,那么在第 \log_2 n-i+1 步中它就会走到右边,否则就会走到左边,而它最终的位置就是由每一步走到左边还是右边来决定的,于是不难得到上面的结论。

于是,我们可以将本来的从上往下递归,改为从下往上递推:

r_i 表示 i 的二进制表示反转后的结果,那么首先我们将 a_ia_{r_i} 互换,先得到了最后的状态。

然后,我们第 i 轮就将连着的两个大小为 2^{i-1} 的块合并成一个大小为 2^i 的块(如之前的图中所示,从 \text{Step}\ 3\text{Step}\ 2 就是把左边两个大小为 4 的块合并成一个大小为 8 的块,右边两个大小为 4 的块也合并成一个大小为 8 的块),这一步和递归 FFT 的实现是一样的。

这样常数略小,而且很容易写,复杂度也非常便于分析(总共 \log_2 n 轮,每轮合并总共 O(n))。

注意在实现时,我们可以直接令当前某一块中的第 i 个下标处存储 \omega_{2^l}^i 的点值(2^l 是当前块长),这样我们将两个函数的运算转化成了两个变量的运算,更加直观。

(四)逆离散傅里叶变换

在下面的讨论中依然认为 n2 的正整数幂次。

考虑如何将多项式的点值表示转换成系数表示。

上面关于单位根的性质讲到了三条,这里再补充一条:

性质 4\sum\limits_{i=0}^{n-1}\omega_n^{ik}=\begin{cases}n&(k=0) \\0&(k\ne 0) \end{cases}

证明:当 k=0 时,\sum\limits_{i=0}^{n-1}\omega_n^{ik}=\sum\limits_{i=0}^{n-1}1=n;而当 k\ne 0 时,由等比数列求和公式有 \sum\limits_{i=0}^{n-1}\omega_n^{ik}=\dfrac{1-\omega_n^{kn}}{1-\omega_n^k},由于 k\ne 0 时分子为 0,所以结果为 0

我们设 a_in-1 次多项式 f(x)x^i 项系数,而 A_i\omega_n^i 的点值,那么用矩阵的形式来表达带入点值的运算如下:

\begin{bmatrix} (\omega_n^0)^0 & (\omega_n^0)^1 & \cdots & (\omega_n^0)^k \\ (\omega_n^1)^0 & (\omega_n^1)^1 & \cdots & (\omega_n^1)^k \\ \cdots & \cdots & \ddots & \cdots \\ (\omega_n^{n-1})^0 & (\omega_n^{n-1})^1 & \cdots & (\omega_n^{n-1})^k \end{bmatrix} \begin{bmatrix} a_0\\a_1\\ \cdots \\ a_{n-1} \end{bmatrix}= \begin{bmatrix} A_0\\A_1\\ \cdots \\ A_{n-1} \end{bmatrix}

记前面这个方阵为 D,我们现在要倒过来通过 A_ia_i,需要找到 D 的逆。

我们先给出结论,再做证明:

\begin{bmatrix} (\omega_n^{-0})^0 & (\omega_n^{-0})^1 & \cdots & (\omega_n^{-0})^k \\ (\omega_n^{-1})^0 & (\omega_n^{-1})^1 & \cdots & (\omega_n^{-1})^k \\ \cdots & \cdots & \ddots & \cdots \\ (\omega_n^{-(n-1)})^0 & (\omega_n^{-(n-1)})^1 & \cdots & (\omega_n^{-(n-1)})^k \end{bmatrix}\times D=nI

我们设左侧的这个方阵为 E,下面尝试证明 ED=nI

对于 ED 的第 i 行第 j 列元素(设为 M_{ij}),可以得到(方便起见,行列从 0n-1 编号):

M_{ij}=\sum\limits_{k=0}^{n-1}E_{ik}\times D_{kj}=\sum\limits_{k=0}^{n-1}\omega_n^{-ik}\times \omega_n^{kj}=\sum\limits_{k=0}^{n-1}\omega_{n}^{(j-i)k}

再根据单位根性质 4,我们得到:当 j-i=0i=j 时,M_{ij}=n,否则 M_{ij}=0,这正是单位矩阵 In 倍,于是得证。

更有趣的是,我们发现:单位根的前三条性质中,将 \omega_n^k 换成 \omega_n^{-k} 后依然全部成立(可以自行验证),所以逆离散傅里叶变换(IDFT)就简单了,只需要在 DFT 过程中将所有的 \omega_n^k 变成 \omega_n^{-k} 即可。

由于 ED=nI,所以最后还需要再将结果除以 n 得到真实的各项系数。

利用同样的优化,即可得到逆快速傅里叶变换(IFFT)

至此,我们已经能够实现多项式乘法了,只需要先将两个多项式 f(x),g(x) 求 FFT,用点值相乘再求 IFFT,即可得到 h=f*g 的各项系数了,时间复杂度为 O(n\log n)

(五)数论变换

在下面的讨论中依然认为 n2 的正整数幂次。

利用快速傅里叶变换求多项式乘法,有一些明显的缺点:

  1. 精度较低,如果系数较大可能丢失精度产生误差(即使给出的系数都是整数);
  2. 无法取模,只支持较小规模的数据(这一点在讨论幂级数的时候更加突出,当我们遇到泰勒展开等运算时需要大量用到阶乘,FFT 难以正常处理)。
  3. 复数运算慢,算法常数大。

基于此,我们需要一种不依赖与复数域进行操作的变换,而它就是数论变换(NTT)

原本我们一直在复数域中讨论,现在我们换一个角度,从数论中模意义下的数域出发讨论。

进行数论变换时,我们得到的所有值都是在 \bmod 一个大素数的意义下的,这个大素数 p 通常需要满足 p=b\times 2^k+1,其中 2^k\ge n,稍后将解释这为什么是必要的。

然后,我们将原本 FFT 中的单位根替换为模 p 的原根(这里不打算像介绍单位根那样系统介绍一遍原根的相关知识了),设 g 是模 p 的某一个原根,设 \omega_n=g^{\frac{p-1}{n}},其余和 FFT 完全一致。

我们知道 FFT 以及 IFFT 的核心在于单位根的四个性质,下面来论证一下这四个性质在我们目前的定义下(在模 p 的数域内)依然成立:

性质 1\omega_{2n}^{2k}=\omega_n^k

证明:\omega_{2n}^{2k}=g^{\frac{p-1}{2n}\times 2k}=g^{\frac{p-1}{n}\times k}=\omega_n^k,即得证。

性质 2\omega_n^k=-\omega_n^{k+\frac{n}{2}}

证明:\omega_{n}^{k+\frac{n}{2}}=\omega_n^k\times \omega_{n}^{\frac{n}{2}},而 \omega_n^{\frac{n}{2}}=g^{\frac{p-1}{n}\times \frac{n}{2}}=g^{\frac{p-1}{2}},由于 g^{p-1}=1g 是模 p 的原根,所以不存在 q<p-1 使得 g^{q}=1,所以 g^{\frac{p-1}{2}}=-1,即得证。

性质 3\omega_{n}^n=1

证明:\omega_n^n=g^{\frac{p-1}{n}\times n}=g^{p-1}=1,即得证。

性质 4\sum\limits_{i=0}^{n-1}\omega_n^{ik}=\begin{cases}n&(k=0) \\0&(k\ne 0) \end{cases}

证明:当 k=0 时,\sum\limits_{i=0}^{n-1}\omega_n^{ik}=\sum\limits_{i=0}^{n-1}1=n;而当 k\ne 0 时,由等比数列求和公式有 \sum\limits_{i=0}^{n-1}\omega_n^{ik}=\dfrac{1-\omega_n^{kn}}{1-\omega_n^k},由于 k\ne 0 时分子为 0,所以结果为 0(和单位根部分的证明完全相同)。

所以这里我们定义的 \omega_n 符合了之前用到的所有性质,用它去做 FFT 和 IFFT,能支持取模,不用写复杂的复数运算,而且常数小,相比 FFT 确实有许多优势,时间复杂度是一样的 O(n\log n)

当然也有不方便用 NTT 而可以用 FFT 做的题,例如题目中出现的系数不是整数时,就难以用 NTT 实现。

因为我们 \omega_n 的定义中用到了 g^{\frac{p-1}{n}},所以 n\mid p-1,而 n 是一个 2 的正整数幂次,所以这就要求 p-1 是一个形如 b\times 2^k 的数了,其中 2^k 要充分大(不小于最大的可能的 n),这样的素数 p 中,最常见的就是 998244353,另一些如 1004535809469762049 也可以作为 NTT 模数,这三个数的最小原根都是 3,比较方便记忆。

当然,也不是所有题目的模数都是这些形如 b\times 2^k+1 的数,遇到了不同的例子,我们就要用更多方法解决。

(六)任意模数意义下的变换

当多项式乘法的模数不再是常见的 b\times 2^k+1 型数时,只做一次 NTT 便不可行了,这里介绍两种常见的解决方法,实际效率都比较慢,也有一些较快的方法,往往实现较为复杂,而且我也不了解,读者可以查阅一些论文。

  1. 三模数 NTT:选取 3 个可做 NTT 的模数(比如上面举出的三个例子),设为 P_1,P_2,P_3,分别进行 NTT,这样我们对于乘积的每一项系数都得到了 3 个模意义下的值 x_1,x_2,x_3,设真实的系数为 x,那么我们有:

    \begin{aligned} x\equiv x_1\bmod P_1 \\ x\equiv x_2\bmod P_2 \\ x\equiv x_3\bmod P_3 \end{aligned}

    我们要求的是 x\bmod pp 是给定的模数),可以发现 x 的上界是 p^2n(每一项都乘满),当 p 在 int 存储范围内时,这个值大约为 10^{23} 左右(这也是为什么选择三个模数做 NTT 就足够的原因),我们只需用中国剩余定理合并三个同余式即可得到 x\bmod p(可以实现得比较精细,并不需要高精度,但可能需要快速乘)。

  2. 分部 FFT:我们发现,只要按照 FFT 做一次,最后再取模就可以了,但是事实上这样不仅数据范围过大,精度也是很差的,所以我们可以通过将系数减小来避免精度损失。

    d=\lceil\sqrt p\rceil,对于原多项式的某个系数 a_i,写成 a_i=kb_i+c_i 的形式,其中 0\leq c_i<k,设 F(x)=kF_1(x)+F_2(x),\ G(x)=kG_1(x)+G_2(x),那么:

    F(x)\times G(x)=k^2F_1(x)G_1(x)+k(F_1(x)G_2(x)+F_2(x)G_1(x))+F_2(x)G_2(x)

    只需要分别计算四个系数较小的多项式乘积,再合并起来即可。

两种方案时间复杂度都是 O(n\log n),但是常数都较大。

(七)分治 FFT

有时我们需要处理的卷积形式比较特殊,例如:

给定一个序列 g_{1,\ldots n}f_0=1,且满足 f_i=\sum\limits_{j=1}^i f_{i-j}g_j,求序列 f_{1,\ldots,n}

此时,我们发现 f 自己也会影响自身后续项的取值,暂且不论求逆等其他做法,单纯使用 FFT 是难以解决这个问题的。

此时我们可以考虑将 FFT 与 CDQ 分治的思想结合。

我们将序列 f_{1,\ldots,n} 分治,设 mid=\dfrac{n+1}{2},先递归计算出 f_{1,\ldots,mid},然后考虑 f_{1,\ldots,mid}f_{mid+1,\ldots,n} 的贡献,计算完后再递归求解 f_{mid+1,\ldots,n} 内部的贡献即可。

我们新设一个序列 h_i=\begin{cases}f_i & (1\leq i\leq mid) \\ 0 & (mid<i\leq n) \end{cases},考虑计算序列 h 和序列 g 的卷积,得到的就是前半部分对后半部分的贡献了。

每一层计算贡献的复杂度是 O(n\log n),因此总的复杂度是 O(n\log^2 n)

(八)高维 FFT

由于这一部分我自己也没写过,所以只能简单讲讲。

我们之前的讨论都是基于一元多项式的,那么对于二元多项式甚至更多元的多项式,如何进行点值表示和系数表示的转换呢?

以二元多项式为例,设最高次数为 x^{n-1}y^{m-1},我们假设 n,m 都是 2 的正整数幂次,我们将系数排成一个 n\times m 的矩阵(行列从 0 开始编号),那么我们需要 n\times m 个点值,对于第 i 行第 j 列将要带入的点值,令它就是 x=\omega_n^i,\ y=\omega_m^j

先考虑每一行,每一行中 x 的次数都是一样的,所以我们主要关心的是 y,如果我们直接把这一行的 m 个数当做一个多项式的系数做一次 FFT,得到的就是一个关于 x^i 的一元多项式的各项系数(i 是这一行的行号)。

接下来,我们再考虑每一列,每一列中 y 的次数都是一样的,而我们将系数从上往下看,它们对应的次数为 x^0,\ldots,x^{n-1},所以我们直接对这一列做一次 FFT,就得到了这一列最终的点值表示结果。

由于对每行每列都做了 FFT,所以时间复杂度为 O(nm(\log n+\log m))

对于更高维的情况也是类似的,只要对每一个维度上都做一维的 FFT,最终就能得到完整的 FFT 结果。设每一维的最高次数分别为 n_1,\ldots,n_k,则复杂度就是 O(\Big(\prod\limits_{i=1}^k n_i\Big)\times \Big(\sum\limits_{i=1}^k \log n_i\Big))

至此,我们初步了解了快速傅里叶变换及其逆变换,数论变换及其逆变换,用它们处理多项式乘法的方法以及一些常见的相关扩展内容,此后的部分中,不再特别区分 FFT 和 NTT,它们都是计算卷积的工具。

卷积的实质是形如 c_x=\sum\limits_{i+j=x}a_ib_j 这样的运算,因此 FFT 并不局限于计算多项式乘法,还可以扩展到其他带有这样卷积形式的式子。

下面试举几例,说明 FFT 在处理卷积问题上的应用(然而与后文关系不大)。

例题 1: [ZJOI 2014] 力

题目大意:

给定 n 个实数 q_1,\ldots,q_n,求出所有 E_i\ (1\leq i\leq n),其中:

E_i=\dfrac{\sum\limits_{j=1}^{i-1}\dfrac{q_i\times q_j}{(i-j)^2}-\sum\limits_{j=i+1}^{n}\dfrac{q_i\times q_j}{(i-j)^2}}{q_i}

(实际上是当质量为 q_i 的质点位于点 i 时,点 i 的加速度,分子为点 i 受到的万有引力,这应该也是题名由来。)

**解题思路:** 首先将分子中约去分母的 $q_i$,得到 $E_i=\sum\limits_{j=1}^{i-1}\dfrac{q_j}{(i-j)^2}-\sum\limits_{j=i+1}^{n}\dfrac{q_j}{(i-j)^2}$,然后将左右两部分分开处理,先看左边。 将 $i$ 拆为 $j+(i-j)$,发现 $j$ 和 $(i-j)$ 恰好是分子和分母的变量,所以自然构造多项式: $$ \begin{aligned} & F(x)=\sum\limits_{i=1}^n q_ix^i,\ \ G(x)=\sum\limits_{i=1}^n\dfrac{1}{i^2} \\ & H(x)=F(x)\times G(x) \end{aligned} $$ 不难验证 $H(x)$ 的 $i$ 次项系数即 $\sum\limits_{j=1}^{i-1}\dfrac{q_j}{(i-j)^2}$,左半部分即通过一次多项式乘法处理完毕。 右半部分因为 $j>i$,所以我们考虑将序列倒过来,把 $n-i$ 拆成 $(n-j)+(j-i)$,于是类似构造多项式: $$ \begin{aligned} & F(x)=\sum\limits_{i=0}^{n-1} q_{n-i}x^i,\ \ G(x)=\sum\limits_{i=1}^n\dfrac{1}{i^2} \\ & H(x)=F(x)\times G(x) \end{aligned} $$ 不难验证 $H(x)$ 的 $n-i$ 次项系数即 $\sum\limits_{j=i+1}^{n}\dfrac{q_j}{(i-j)^2}$,右半部分也通过多项式乘法解决。 从而将两部分相加,即得到了所求的 $E_i$。 时间复杂度:$O(n\log n)$。 --- ### 例题 $2$:[残缺的字符串](https://www.luogu.com.cn/problem/P4173) **题目大意:** 给定长度为 $m$ 的模式串 A 和长度为 $n$ 的文本串 B,两者都可能带有通配符(可以匹配任意字符),求 A 在 B 中所有匹配。 $1\leq m,n\leq 3\times 10^5$。 **解题思路:** 带通配符的匹配,用 KMP 等字符串算法不太好做,此时考虑一种基于 FFT 的字符串匹配算法: 两个字符串 $s_{1\ldots n}$ 和 $t_{1\ldots n}$ 相等,当且仅当每个对应位置字符都相等,也就是: $$ \sum\limits_{i=1}^n(s_i-t_i)^2=0 $$ 如果 $t_{1\ldots m}$ 与 $s$ 的一个子串 $s_{a\ldots a+m-1}$ 相等,也有类似的等式,所以我们尝试通过构造多项式乘法式子来快速检验,令: $$ \begin{aligned} & F(x)=\sum\limits_{i=1}^m t_ix^i,\ \ G(x)=\sum\limits_{i=0}^{n-1} s_{n-i}x^i \\ & H(x)=F(x)\times G(x) \end{aligned} $$ 我们考虑 $H(x)$ 的 $k$ 次项系数(其中 $m\leq k\leq n$),它是 $\sum\limits_{i=1}^m t_is_{n-(k-i)}=\sum\limits_{i=1}^m t_is_{n-k+i}$,我们发现,如果 $2[x^k]H(x)=\sum\limits_{i=1}^mt_i^2+s_{n-k+i}^2$,那么就等价于上面 $s$ 的这个子串和 $t$ 相等的判定式。 于是我们可以在 $O(n\log n)$ 的复杂度内实现字符串匹配,如果带了通配符,我们只需要改写一下判定式即可: $$ \sum\limits_{i=1}^ns_it_i(s_i-t_i)^2=0 $$ 其中通配符对应的数字为 $0$,正确性显然,因为某一位上如果有通配符,这一项自然就是 $0$ 了。 将上面的式子展开,发现主要是要计算 $\sum\limits_{i=1}^ns_i^2t_i,\ \sum\limits_{i=1}^ns_it_i^2$ 这两项,都可以用和上面类似的方法转换成多项式乘法来做。 时间复杂度:$O((m+n)\log (m+n))$。 --- ## 二、一些多项式技巧 --- 这一部分将会介绍:多项式单点求值,拉格朗日插值及其扩展,多项式除法和取模,多项式快速多点求值,多项式快速插值,这些内容基本都只适用于多项式,在更广义的形式幂级数中难以定义,所以单独放在了这一板块。 讲多项式除法的时候需要用到求逆,但是只会用到这个运算本身,不会涉及求逆的具体过程,如果要了解求逆的具体过程可以看第三部分。 某些基础内容比 FFT 要容易理解得多,但是鉴于多点求值和快速插值需要建立在快速乘法的基础上做,这一章还是放在了 FFT 之后。 ### (一)秦九韶算法 给定一个多项式 $F(x)=\sum\limits_{i=0}^{n-1} a_ix^i$ 和一个数 $t$,求 $F(t)$。 暴力做是 $O(n^2)$ 的,考虑进行优化。 一种优化是在枚举 $i=0,\ldots,n-1$ 的过程中始终维护一个 $prod=p^i$。 另外我们也可以从高位入手,考虑枚举 $i=n-1,\ldots,0$,维护一个 $F_{i}(t)=\sum\limits_{j=i}^{n-1}a_jt^{j-i}$。 那么在 $i$ 变小的过程中考虑 $F_i(t)$ 的变化,应该为 $F_i(t)=F_{i+1}(t)\times t+a_i$,边界可以设为 $F_n(t)=0$,容易验证这个过程是正确的。 而 $F(t)=F(0)$,这样我们就可以在 $O(n)$ 的时间复杂度内计算 $F(t)$ 的值。 这个过程就是**秦九韶算法**,它最经典的应用就是基于 `getchar` 的快速读入,回忆快读的原理,其实就是 $F_i(t)$ 与 $F_{i+1}(t)$ 之间的这个转移关系。 ### (二)拉格朗日插值法 之前在介绍 FFT 的时候讲到了多项式系数表示和点值表示的转换,从点值到系数的这个过程称为插值,那么 FFT 的限制就是只能针对特殊的点值进行(只能对单位根/原根进行插值)。 如果暴力对系数列方程组进行求解,那么时间复杂度是 $O(n^3)$ 的高斯消元。 而对于任意点值,我们有一种容易实现的在 $O(n^2)$ 复杂度内插值的方案。 考虑给出的 $n$ 个点值为 $(x_0,y_0),(x_1,y_1),\ldots,(x_{n-1},y_{n-1})$,考虑构造多项式 $F_i(x)$,使得 $F_{i}(x_i)\ne 0$,而对于 $j\ne i$,有 $F_i(x_j)=0$,那么考虑多项式: $$ F(x)=\sum\limits_{i=0}^{n-1} F_i(x)\times \dfrac{y_i}{F_i(x_i)} $$ 这就是我们插值需要得到的多项式了,因为对于 $y_i$ 这一项,所有 $x_i$ 中只有 $F_i(x_i)$ 这个系数不为 $0$。 接下来考虑 $F_i(x)$ 怎么求,我们可以用简单暴力的方案: $$ F_i(x)=\prod\limits_{j\ne i}(x-x_j) $$ 这样一来,当然就满足了:对于 $j\ne i$,$F_i(x)=0$,而 $F_i(x_i)\ne 0$ 了。 结合以上两式,得到了最终 $F(x)$ 的表达式: $$ F(x)=\sum\limits_{i=0}^{n-1}y_i\times \dfrac{\prod\limits_{j\ne i}(x-x_j)}{\prod\limits_{j\ne i}(x_i-x_j)} $$ 这是一个 $n-1$ 次多项式(注意分母上的都是常数),而且以上形式中各项是齐次的。 上面介绍的这种 $O(n^2)$ 插值算法就是**拉格朗日插值法**,接下来介绍几个拉格朗日插值中的 trick。 1. 自变量连续的拉格朗日插值。 如果给定的 $n$ 个点值具有 $(1,y_1),\ldots,(n,y_n)$ 这样的形式,那么插值可以进一步优化至 $O(n)$。 先写出拉格朗日插值的式子: $$ F(x)=\sum\limits_{i=1}^{n}y_i\times \dfrac{\prod\limits_{j\ne i}(x-j)}{\prod\limits_{j\ne i}(i-j)} $$ 我们可以先预处理 $M=\prod\limits_{i=1}^n (x-i)$,并将分母上的式子写成两个阶乘的形式,所以: $$ F(x)=\sum\limits_{i=1}^{n}y_i\times \dfrac{M}{(k-x_i)\times (i-1)!\times (-1)^{n-i}(n-i)!} $$ 这个式子就可以 $O(n)$ 计算了。 2. 重心拉格朗日插值。 如果我们要支持维护一个带插入点值的多项式,那么普通的拉格朗日插值就必须每次推倒重做,时间复杂度会到达 $O(n^3)$ 级别。 下面对拉格朗日插值的式子做一些改写: $$ F(x)=\sum\limits_{i=0}^{n-1}y_i\times \dfrac{\prod\limits_{j\ne i}(x-x_j)}{\prod\limits_{j\ne i}(x_i-x_j)}=\prod\limits_{j=0}^{n-1}(x-x_j)\times\Big(\sum\limits_{i=0}^{n-1}\dfrac{y_i}{(x-x_i)\prod\limits_{j\ne i}(x_i-x_j)}\Big) $$ 注意这里需要确保 $x\ne x_i$,否则直接带入给定的点值即可。 考虑式子中的 $\prod\limits_{j\ne i}(x_i-x_j)$,我们只要在每次插入点值后 $O(n)$ 处理所有这样的式子的修改,就可以实现 $O(n)$ 的单次插入了。 而这是容易的,设插入点值 $(x_n,y_n)$,对于原本的 $i<n$,$\prod\limits_{j\ne i}(x_i-x_j)$ 中多了一个 $(x_i-x_n)$,而对于 $i=n$ 来说,我们暴力 $O(n)$ 计算这个式子的值即可。 每次查询点值的复杂度也可以做到 $O(n)$,只要 $O(n)$ 枚举式子中的 $i$,并 $O(n)$ 计算出 $\prod\limits_{j=0}^{n-1}(x-x_j)$ 即可。 --- #### 例题 $3$:[[CF 622 F] The Sum of the k-th Powers](https://www.luogu.com.cn/problem/CF622F) **题目大意:** 给定 $n,k$,求出 $\sum\limits_{i=1}^n i^k$ 模 $10^9+7$ 的结果。 $1\leq n\leq 10^9,\ 1\leq k\leq 10^6$。 **解题思路:** 可以证明:$\sum\limits_{i=1}^n i^k$ 是以 $n$ 为自变量的 $k+1$ 次多项式(离散地看,其差分序列为 $k$ 次式)。 所以我们只需要用 $1,2,\ldots,k+2$ 这些点值带入,通过拉格朗日插值即可算出题目所求的式子,注意使用自变量连续的优化版拉格朗日插值。 时间复杂度:$O(k)$。 --- ### (三)多项式除法和取模 之所以在强调是多项式除法而非形式幂级数除法,是因为这里做的是带余除法,并非简单的求逆。 形式化地说,给定多项式 $F(x)$ 和 $P(x)$,次数分别为 $n,m\ (n\ge m)$,求出多项式 $Q(x)$ 和 $R(x)$ 使得 $F(x)=P(x)Q(x)+R(x)$,其中 $R(x)$ 的次数小于 $m$。 考虑对于 $n$ 次多项式 $F(x)$,定义: $$ F_R(x)=x^nF(\dfrac{1}{x}) $$ 稍加分析即可发现,新多项式的 $i$ 次项系数就是原多项式的 $n-i$ 次项系数。 考虑将 $\dfrac{1}{x}$ 带入原式: $$ F(\dfrac{1}{x})=P(\dfrac{1}{x})Q(\dfrac{1}{x})+R(\dfrac{1}{x}) $$ 然后两边同时乘上 $x^n$: $$ x^nF(\dfrac{1}{x})=x^mP(\dfrac{1}{x})x^{n-m}Q(\dfrac{1}{x})+x^nR(\dfrac{1}{x}) $$ 然后发现这里面就有大量 $F_R(x)$ 的形式,可以化简成: $$ F_R(x)=P_R(x)Q_R(x)+x^{n-m+1}R_R(x) $$ 注意这里我们将 $Q(x)$ 看成 $n-m$ 次多项式,而 $R(x)$ 看成 $m-1$ 次多项式,实际情况不会大于这个值。 由于要求的 $Q(x)$ 只有 $n-m$ 次,所以我们不妨将两边同时对 $x^{n-m+1}$ 取模(也就是仅保留 $n-m+1$ 以下次项,在介绍形式幂级数时还将讲到这个取模的含义),由于 $R_R(x)$ 前有 $x^{n-m+1}$ 的系数,所以在 $\bmod x^{n-m+1}$ 意义下就消失了: $$ F_R(x)\equiv P_R(x)Q_R(x)\bmod x^{n-m+1} $$ 于是我们可以根据求逆(可参考第三部分第四节)和乘法算出 $Q_R(x)\equiv F_R(x)\times P_R^{-1}(x)\bmod x^{n-m+1}$。 再根据 $R(x)=F(x)-P(x)Q(x)$ 求出 $R(x)$,就求出了多项式除以多项式的商和余式。 ### (四)多项式快速求值与插值 给定 $n-1$ 次多项式,求出它在 $m$ 个点的取值;以及给定 $n$ 对点值,求 $n-1$ 次多项式,这两个问题分别可以用秦九韶算法和拉格朗日插值做到 $O(nm)$ 和 $O(n^2)$ 的复杂度,之类我们依据快速傅里叶变换和倍增思想做进一步的优化。 先考虑**多项式快速多点求值**。 考虑求出 $n$ 次多项式 $F(x)$ 在 $a_1,\ldots,a_m$ 这 $m$ 个点的值($n,m$ 同阶),设 $s=\lfloor\dfrac{m}{2}\rfloor$,首先构造: $$ P_0(x)=\prod\limits_{i=1}^{s}(x-a_i),\ \ P_1(x)=\prod\limits_{i=s+1}^m(x-a_i) $$ 设 $F(x)$ 除以 $P_0(x),\ P_1(x)$ 的商分别为 $Q_0(x),\ Q_1(x)$,余式分别为 $R_0(x),\ R_1(x)$,那么对于 $i\in [1,s]$,有 $P_0(a_i)=0$,所以: $$ F(a_i)=P_0(a_i)\times Q_0(a_i)+R_0(a_i)=R_0(a_i) $$ 因此我们只需要递归求解 $R_0(a_i)$ 即可,其中 $R_0(x)$ 是一个不超过 $\dfrac{m}{2}$ 次的多项式,$R_1(x)$ 那一侧同理。 所以,我们可以将一个规模为 $m$ 的问题分解成两个规模为 $\dfrac{m}{2}$ 的问题,每次分解的代价是两次多项式除法,即 $O(m\log m)$,所以这部分的总复杂度是 $O(m\log^2 m)$。 注意还要计算 $P_0(x)$ 和 $P_1(x)$,这两个却不难算,利用一个分治乘法的技巧即可。我们考虑 $P_{l,r}(x)=\prod\limits_{i=l}^r (x-a_i)$,那么 $P_{l,r}(x)=P_{l,mid}(x)\times P_{mid+1,r}(x)$,一样分治就可以算出,合并的代价是一次乘法的 $O(m\log m)$,所以这一部分的总复杂度也是 $O(m\log^2 m)$。 至此,我们在 $O(m\log^2 m)$ 的复杂度内做出了多项式的多点求值。 再考虑**多项式快速插值**。 给定 $n$ 对点值 $(x_0,y_0),\ldots,(x_{n-1},y_{n-1})$,求出一个 $n-1$ 次多项式 $F(x)$ 使得对于所有 $i$ 有 $F(x_i)=y_i$。 那么直接套拉格朗日插值公式: $$ F(x)=\sum\limits_{i=0}^{n-1}y_i\times \dfrac{\prod\limits_{j\ne i}(x-x_j)}{\prod\limits_{j\ne i}(x_i-x_j)} $$ 下面要做的就是对它进行优化,我们设 $P(x)=\prod\limits_{i=0}^{n-1}(x-x_i)$,那么: $$ F(x)=\sum\limits_{i=0}^{n-1}y_i\times \Big(\prod\limits_{j\ne i}(x-x_j)\Big)\times \dfrac{(x_i-x_i)}{P(x_i)} $$ 右边的东西很奇怪,是 $\dfrac{0}{0}$ 的形式,我们可以用洛必达法则来处理: $$ \dfrac{x_i-x_i}{P(x_i)}=\lim\limits_{x\to x_i}\dfrac{x-x_i}{P(x)}=\lim\limits_{x\to x_i}\dfrac{1}{P'(x)}=\dfrac{1}{P'(x_i)} $$ 注意我们只是在计算过程中借用极限处理的洛必达法则,实际运算中是不涉及极限的。 整理一下刚才得到的结果: $$ F(x)=\sum\limits_{i=0}^{n-1}y_i\times \Big(\prod\limits_{j\in [0,n-1],j\ne i}(x-x_j)\Big)\times \dfrac{1}{P'(x_i)} $$ 设 $(x_l,y_l),\ldots,(x_r,y_r)$ 这些点值按照上面那个式子算出 $r-l$ 次多项式 $F_{l,r}(x)$(注意并不是这些点值插值得到的多项式),即: $$ F_{l,r}(x)=\sum\limits_{i=l}^{r}y_i\times \Big(\prod\limits_{j\in [l,r],j\ne i}(x-x_j)\Big)\times \dfrac{1}{P'(x_i)} $$ 考虑找出 $F_{l,r}(x)$ 与 $F_{l,mid}(x),\ F_{mid+1,r}(x)$ 的关系: $$ F_{l,r}(x)=\Big(\sum\limits_{i=l}^{mid}\dfrac{y_i}{P'(x_i)}\times \prod\limits_{j\in [l,r],j\ne i}(x-x_j)\Big)+\Big(\sum\limits_{i=mid+1}^{r}\dfrac{y_i}{P'(x_i)}\times \prod\limits_{j\in [l,r],j\ne i}(x-x_j)\Big) $$ 取左边那个括号为例,进行拆分: $$ \begin{aligned} \Big(\sum\limits_{i=l}^{mid}\dfrac{y_i}{P'(x_i)}\times \prod\limits_{j\in [l,r],j\ne i}(x-x_j)\Big) & =\prod\limits_{j\in [mid+1,r]}(x-x_j)\times \Big(\sum\limits_{i=l}^{mid}\dfrac{y_i}{P'(x_i)}\times \prod\limits_{j\in [l,mid],j\ne i}(x-x_j)\Big) \\ & =\prod\limits_{j\in [mid+1,r]}(x-x_j)\times F_{l,mid}(x) \end{aligned} $$ 右边括号类似,于是我们就得到了一个比较简单的式子: $$ F_{l,r}(x)=\prod\limits_{j\in [mid+1,r]}(x-x_j)\times F_{l,mid}(x)+\prod\limits_{j\in [l,mid]}(x-x_j)\times F_{mid+1,r}(x) $$ 考虑一下我们要计算哪些东西: - 边界情况 $F_{i,i}(x)=\dfrac{y_i}{P'(x_i)}$,也就是要计算 $P'(x)$ 的表达式(通过分治乘法和多项式的求导法则,其中求导在第三部分有介绍)并进行多点求值,时间复杂度为 $O(n\log^2 n)$; - 迭代过程中的 $\prod\limits_{j\in [l,r]}(x-x_j)$,通过分治乘法计算,时间复杂度为 $O(n\log^2 n)$; - 进行 $F_{l,r}(x)$ 的计算,这是和上面的分治乘法同步进行的,时间复杂度也是 $O(n\log^2 n)$。 所以整个插值过程的复杂度就是 $O(n\log^2 n)$。 --- 至此我们大致了解的多项式的带余除法以及求值插值如何实现,拉格朗日插值等知识点是非常重要的,在许多题目中可以把 $O(n^3)$ 优化到 $O(n^2)$ 甚至 $O(n)$,相比而言快速求值和插值反而用得更少。 --- ## 三、形式幂级数基础 --- 这一部分将会介绍:形式幂级数的定义和计算,泰勒展开和牛顿迭代法,常见形式幂级数运算的实现。 对于数列 $f_i\ (i\in \mathbb{N})$,定义其**形式幂级数** $F(x)$ 为: $$ F(x)=\sum\limits_{i=0}^{\infty} f_ix^i $$ 与一般意义上的多项式不同,形式幂级数允许无限项的求和,而在数学推导后对各种操作进行实现时,我们通常只能取其前 $n$ 项进行计算,此时应表示成: $$ F(x)\bmod x^n $$ 以表示 $F(x)$ 的 $0$ 到 $n-1$ 次项组成的多项式。 更关键的一点是:形式幂级数中 $x$ 的取值是不研究的,也就是说 $F(x)$ 只是 $f_i$ 的一种表现形式,而一般不视作 $x$ 的函数。在进行各种运算时,我们均假定 $F(x)$ 收敛(如果你目前不清楚这代表什么也没有关系)。 ### (一)基本运算 定义形式幂级数的几个基本运算如下(多项式运算的扩展),设 $A(x)=\sum\limits_{i=0}^{\infty}a_ix^i,\ B(x)=\sum\limits_{i=0}^{\infty}b_ix^i$: 1. **两形式幂级数相等:** $$ A(x)=B(x)\Leftrightarrow \forall i\in \mathbb{N},\ a_i=b_i $$ 即两个幂级数每一项的系数都相等。 2. **两形式幂级数相加减:** $$ A(x)\pm B(x)=\sum\limits_{i=0}^{\infty} (a_i\pm b_i)x^i $$ 容易发现,形式幂级数加法具有交换律和结合律,且 $0$ 为加法单位元。 3. **两形式幂级数相乘(卷积):** $$ (A*B)(x)=\sum\limits_{i=0}^{\infty}(\sum\limits_{j=0}^ia_jb_{i-j})x^i $$ 和多项式乘法的定义基本相同,只是拓展到了无限项。 容易发现,形式幂级数卷积具有交换律,结合律以及对加法的分配率,且 $1$ 为乘法单位元,如果两个形式幂级数 $A(x),B(x)$ 的乘积在 $\bmod x^n$ 意义下为 $1$,则称它们在 $\bmod x^n$ 的意义下互为**乘法逆元**。 4. **形式幂级数求导:** $$ A'(x)=\sum\limits_{i=0}^{\infty}(i+1)a_{i+1}x^i $$ 导函数 $A'(x_0)$ 也可以写为 $\dfrac{\mathrm{d}A}{\mathrm{d}x}(x_0)$,高阶导数写作 $A^{(k)}(x)$,即 $A^{(k)}(x)=(A^{(k-1)})'(x)$。 加法运算:$(A+B)'=A'+B'$; 乘法运算:$(AB)'=A'B+AB'$; 复合运算:$\dfrac{\mathrm{d}B}{\mathrm{d}x}=\dfrac{\mathrm{d}B}{\mathrm{d}A}\times \dfrac{\mathrm{d}A}{\mathrm{d}x}$,即若 $C(x)=B(A(x))$,则 $C'(x)=B'(A(x))A'(x)$。 5. **形式幂级数积分:** $$ \int A(x)\mathrm{d}x=\Big(\sum\limits_{i=1}^{\infty}\dfrac{a_{i-1}}{i}x^i\Big)+C $$ 此为不定积分,定积分类似,设 $B(x)=\displaystyle\int A(x)\mathrm{d}x$,则 $\displaystyle\int_{x_1}^{x_2} A(x)\mathrm{d}x=B(x_2)-B(x_1)$。 所以,如果要去除最后的常数 $C$,应该计算 $\displaystyle\int_0^{x_0}A(x)\mathrm{d}x$。 求导和积分互为逆运算,但是注意求导后积分可能会失去常数项信息。 考虑在 $\bmod x^n$ 的意义下实现以上运算,加减法以及导数积分都是可以轻松做到 $O(n)$ 的,而计算卷积时利用上一章讲到的 FFT,可以做到 $O(n\log n)$ 的复杂度。 ### (二)泰勒展开 对于一个解析式和图像可能比较奇怪或难以处理的函数 $G(x)$,我们选取一点 $x_0$,采用一个形式幂级数 $F(x)$ 来在 $x_0$ 的某一邻域内拟合 $G(x)$ 的图像,这个过程就叫做**泰勒展开**。 更具体地说,对于曲线 $G(x)$,如果它在 $x=x_0$ 处具有 $n$ 阶导数,那么可以用关于 $x-x_0$ 的 $n$ 次多项式 $F(x)$ 来在 $x_0$ 的某一邻域内逼近 $G(x)$ 的值,泰勒公式保证了 $G(x)-F(x)$ 是 $(x-x_0)^n$ 的高阶无穷小。 我们先从较简单的形式开始,先令 $x_0=0$。 考虑形式幂级数 $F(x)$ 的各阶导数,我们希望对于任意 $i\in [0,n]$ 中的整数,都有 $F^{(i)}(0)=G^{(i)}(0)$。 先考虑 $n$ 阶导数(设 $a_i$ 为 $F(x)$ 的各项系数): $$ F^{(n)}(0)=n\times (n-1)\times \ldots\times 1\times a_n=G^{(n)}(0) $$ 所以我们可以得到: $$ a_n=\dfrac{G^{(n)}(0)}{n\times (n-1)\times \ldots\times 1}=\dfrac{G^{(n)}(0)}{n!} $$ 类似地,再考虑 $n-1$ 阶导数: $$ F^{(n-1)}(0)=(n-1)!a_{n-1}+\dfrac{n!}{1!}a_n\times 0=(n-1)!a_{n-1}=G^{(n-1)}(0) $$ 所以我们可以得到: $$ a_{n-1}=\dfrac{G^{(n-1)}(0)}{(n-1)!} $$ 以此类推,我们可以得到,对于每一个 $i\in [0,n]\cap \mathbb{Z}$,都有 $a_i=\dfrac{G^{(i)}(0)}{i!}$,所以: $$ F(x)=\sum\limits_{i=0}^n \dfrac{G^{(i)}(0)}{i!}x^i $$ 当 $n\to \infty$ 时,即: $$ F(x)=\sum\limits_{i=0}^{\infty} \dfrac{G^{(i)}(0)}{i!}x^i $$ 这个式子是泰勒展开 $x_0=0$ 的特殊情形,称为**麦克劳林公式**。 当 $x_0\ne 0$ 时,我们只需将新的 $F(x)$ 设为原来的 $F(x-x_0)$ 即可,因为我们只需要保证求 $i$ 次导时只有 $i$ 次项系数会影响结果,那么将原来的 $x$ 换成 $(x-x_0)$ 后恰有 $F(x)$ 中每个 $(x-x_0)^i\ (i>0)$ 都是 $0$,所以: $$ F(x)=\sum\limits_{i=0}^{\infty} \dfrac{G^{(i)}(x_0)}{i!}(x-x_0)^i $$ 当 $n$ 趋于无穷时,我们就可以认为在 $x_0$ 的某一邻域内,$G(x)=F(x)$,但是别忘了,形式幂级数的研究基于 $F(x)$ 收敛的假设,所以我们不考虑 $G(x)-F(x)$ 是否发散,当成收敛来处理。 接下来介绍几个泰勒展开的实例。 1. **指数函数 $G(x)=e^x$。** 由于 $(e^x)'=e^x$,所以 $G^{(i)}(x)=G(x)=e^x$,取 $x_0=0$,则 $G^{(i)}(x)=1$,因此: $$ e^x=\sum\limits_{i=0}^{\infty}\dfrac{x^i}{i!} $$ 当我们取 $x=1$ 时,即可以得到一个非常常见的关于 $e$ 的等式:$e=\sum\limits_{i=0}^{\infty} \dfrac{1}{i!}$。 2. **对数函数 $G(x)=\ln (x)$。** 由于 $(\ln (x))'=\dfrac{1}{x}$,接下来再根据 $x^{\mu}$ 的求导法则即可得到 $G^{(i)}(x)=(-1)^{i-1}\times (i-1)!\times x^{-i}$,其中的 $(i-1)!$ 可以与泰勒展开中的 $i!$ 抵消,只剩下了 $\dfrac{1}{i}$。 我们取 $x_0=1$ 进行展开(因为 $\ln (x)$ 在 $x=0$ 处不存在导数): $$ \ln (x)=\sum\limits_{i=0}^{\infty}(-1)^{i-1}\dfrac{(x-1)^i}{i} $$ 稍作变换即可得到一个我们更为熟悉的式子: $$ \ln (1+x)=\sum\limits_{i=0}^{\infty}(-1)^{i-1}\dfrac{x^i}{i} $$ (实际上这个展开式的收敛半径为 $1$,但我们在进行形式幂级数的研究时不太关注这一点) 3. **正弦/余弦函数 $G(x)=\sin (x)$ 及 $G(x)=\cos (x)$。** 对正弦或余弦函数求导,我们可以得到一个循环:$\sin\to \cos\to -\sin\to -\cos\to \sin\to \cdots$。 取 $x_0=0$,则 $\sin (x_0)=0,\ \cos(x_0)=1$,在 $\sin (x)$ 展开式中,就只有奇数次项是与 $\cos$ 有关的,而与 $\sin$ 有关的直接根据 $\sin(x_0)=0$ 忽略,在 $\cos(x)$ 中同理只有偶数次项和 $\cos$ 有关,所以: $$ \begin{aligned} \sin(x)=\sum\limits_{i=0}^{\infty}(-1)^i\dfrac{x^{2i+1}}{(2i+1)!}=x-\dfrac{x^3}{3!}+\dfrac{x^5}{5!}-\cdots \\ \cos(x)=\sum\limits_{i=0}^{\infty}(-1)^i\dfrac{x^{2i}}{(2i)!}=1-\dfrac{x^2}{2!}+\dfrac{x^4}{4!}-\cdots \end{aligned} $$ ### (三)牛顿迭代法 考虑两个幂级数的复合运算定义如下: 若 $A(x)=\sum\limits_{i=0}^{\infty}a_ix^i,\ B(x)=\sum\limits_{i=0}^{\infty}b_ix^i,\ C(x)=B(A(x))$,则 $$ C(x)=\sum\limits_{i=0}^\infty b_i(\sum\limits_{j=0}^\infty a_jx^j)^i $$ 也就是直接将一个幂级数作为另一个幂级数的 $x$ 处理。 有时我们会遇到这样的问题:给定一个幂级数 $B(x)\bmod x^n$,求出一个幂级数 $A(x)\bmod x^n$ 使得: $$ B(A(x))\equiv 0\bmod x^n $$ 当然,提出这个问题的前提是 $B(A(x))$ 收敛,$A(x)$ 在 $\bmod x^n$ 意义下唯一(或者直接将复合定义式处求和上界视为 $n$)。 处理这样的问题的一种通用方法就是牛顿迭代法。 首先假设我们通过某种方式求出了 $n=1$ 时的答案(即确定了常数项),在实际运用中这通常是容易的。 当 $n>1$ 时,我们先递归求解出 $\lceil \dfrac{n}{2}\rceil $ 时的答案(接下来为了方便书写,省略了上取整),称为 $A_0(x)$,即: $$ B(A_0(x))\equiv 0\bmod x^{\frac{n}{2}} $$ 接下来考虑对 $B(A(x))$ 这个复合函数进行泰勒展开,取 $A_0(x)$ 为展开的点: $$ B(A(x))=\sum\limits_{i=0}^\infty \dfrac{B^{(i)}(A_0(x))}{i!}(A(x)-A_0(x))^i $$ 这一步和普通函数的泰勒展开是没区别的,只是 $x$ 变为 $A(x)$,而 $x_0$ 变为 $A_0(x)$。 这个式子看似棘手,实际上方便处理。因为我们有 $A(x)\equiv A_0(x)\bmod x^{\frac{n}{2}}$(根据 $A_0(x)$ 的假设而来),移项后平方,得到: $$ (A(x)-A_0(x))^2\equiv 0\bmod x^n $$ 因此上面的展开式中大于等于二次的项都可以忽略了(在 $\bmod x^n$ 意义下为 $0$),也就是说: $$ B(A(x))\equiv B(A_0(x))+B'(A_0(x))(A(x)-A_0(x))\bmod x^n $$ 而条件是 $B(A(x))\equiv 0\bmod x^n$,所以: $$ B(A_0(x))+B'(A_0(x))A(x)-B'(A_0(x))A_0(x)\equiv 0\bmod x^n $$ 这里面只有 $A(x)$ 是未知量,解得: $$ A(x)\equiv A_0(x)-\dfrac{B(A_0(x))}{B'(A_0(x))} \bmod x^n $$ 就建立了从 $A_0(x)$ 到 $A(x)$ 的过程,迭代这一过程即可求解原先的 $A(x)$。 ### (四)形式幂级数的更多运算 有了牛顿迭代法,我们可以处理更多的形式幂级数运算。 1. **形式幂级数求逆。** 给定一个幂级数 $G(x)\bmod x^n$,求一个幂级数 $F(x)\bmod x^n$ 使得 $G(x)F(x)\equiv 1\bmod x^n$。 注意:$G(x)$ 可逆的条件是其常数项存在逆元,$[x^0]G(x)\ne 0$,那么 $F(x)$ 的常数项就是 $G(x)$ 的常数项的乘法逆元了,这是接下来迭代的下界。 考虑先递归求出一个 $F_0(x)$ 使得: $$ F_0(x)G(x)\equiv 1\bmod x^{\frac{n}{2}} $$ 接下来有两种处理手法。 1. 平方法。 根据 $F(x)\equiv F_0(x)\bmod x^{\frac{n}{2}}$,我们知道: $$ (F(x)-F_0(x))^2\equiv 0\bmod x^n $$ 先展开平方,再给两边同时乘个 $G(x)$,右边是 $0$ 就不用管了,看看左边的形式: $$ G(x)F^2(x)-2G(x)F(x)F_0(x)+G(x)F_0^2(x)\equiv 0\bmod x^n $$ 根据条件 $F(x)G(x)\equiv 1\bmod x^n$,可以化简: $$ F(x)-2F_0(x)+G(x)F_0^2(x)\equiv 0\bmod x^n $$ 再根据这个解出 $F(x)$: $$ F(x)\equiv 2F_0(x)-G(x)F_0^2(x)\bmod x^n $$ 按此式子迭代即可求出要求的 $F(x)$。 2. 牛顿迭代法。 设 $H(F(x))=F(x)G(x)-1$,则我们要求的就是 $H(F(x))\equiv 0\bmod x^n$ 的 $F(x)$,直接套之前写过的牛顿迭代法的式子: $$ \begin{aligned} G(x)F_0(x) & \equiv 1\bmod x^{\frac{n}{2}} \\ F(x) & \equiv F_0(x)-\dfrac{H(F_0(x))}{H'(F_0(x))}\bmod x^n \end{aligned} $$ 考虑 $H'(F(x))=G(x)$,而因为泰勒展开中 $H'(F_0(x))$ 后的乘积项是 $(F(x)-F_0(x))\equiv 0\bmod x^{\frac{n}{2}}$,所以我们这里的 $H'(F_0(x))$ 在 $\bmod x^n$ 意义下只有前 $\dfrac{n}{2}$ 次项有意义,由于 $G(x)\equiv \dfrac{1}{F_0(x)}\bmod x^{\frac{n}{2}}$,所以可得: $$ \begin{aligned} F(x) & \equiv F_0(x)-(G(x)F_0(x)-1)\times F_0(x)\bmod x^n \\ F(x) & \equiv 2F_0(x)-G(x)F_0^2(x)\bmod x^n \end{aligned} $$ 同样迭代即可。 这样迭代的时间复杂度为 $O(n\log n+\dfrac{n}{2}\log \dfrac{n}{2}+\ldots)$,内部的和式不超过 $2n\log n$,所以该算法时间复杂度为 $O(n\log n)$。 2. **形式幂级数对数函数(求 $\ln$):** 给定一个幂级数 $G(x)\bmod x^n$,求出一个幂级数 $F(x)\bmod x^n$ 使得 $F(x)\equiv \ln(G(x))\bmod x^n$。 这里的 $\ln(G(x))$ 就是 $G(x)$ 与 $\ln$ 的麦克劳林级数复合。 注意,求 $\ln$ 时要求满足 $[x^0]G(x)=1$,否则会出现 $\ln(c)$ 在模意义下无法求解的问题(而只有 $\ln(1)$ 是能做的,常数项是 $1$ 时可以正常做,当然这个事情也许还会牵涉到更多有关收敛性的问题,我也不甚了解)。 这个问题其实比较简单,我们将上面要求的式子两边同时求导: $$ F'(x)\equiv \dfrac{G'(x)}{G(x)}\bmod x^n $$ 注意 $\ln’(x)=\dfrac{1}{x}$,而根据链式法则做复合函数求导时不要忘记 $G’(x)$。 此时我们已经去掉了 $\ln$,然后两边积分还原 $F(x)$: $$ F(x_0)\equiv \int_0^{x_0}\dfrac{G'(x)}{G(x)}\mathrm{d}x $$ 注意常数项为 $\ln(1)=0$。 于是我们先做一次求导,然后求一次逆,然后做一次多项式乘法,最后积分即可,时间复杂度为 $O(n\log n)$。 3. **形式幂级数指数函数(求 $\exp$):** 给定一个幂级数 $G(x)\bmod x^n$,求出一个幂级数 $F(x)\bmod x^n$ 使得 $F(x)\equiv \exp(G(x))\bmod x^n$。 同理,这里的 $\exp(G(x))$ 就是 $G(x)$ 与 $\exp$ 的麦克劳林级数复合,且基于同样的理由,要求 $[x^0]G(x)=0$。 考虑用牛顿迭代法解决这个问题,设 $H(F(x))=\ln (F(x))-G(x)$。 直接带式子: $$ \begin{aligned} F_0(x) & \equiv \exp(G(x))\bmod x^{\frac{n}{2}} \\ F(x) & \equiv F_0(x)-\dfrac{H(F_0(x))}{H'(F_0(x))} \end{aligned} $$ 而 $H'(F_0(x))=\dfrac{1}{F_0(x)}$,注意这里的求导仅仅是 $\dfrac{\mathrm{d}H}{\mathrm{d}F_0}$,所以不用链式法则。 于是我们有: $$ \begin{aligned} F(x) & \equiv F_0(x)-\dfrac{\ln(F_0(x))-G(x)}{\frac{1}{F_0(x)}} \\ F(x) & \equiv F_0(x)-F_0(x)\ln(F_0(x))+F_0(x)G(x) \\ F(x) & \equiv F_0(x)\Big(1-\ln(F_0(x))+G(x)\Big) \end{aligned} $$ 最后一个式子就是我们的目标,按照它迭代运算即可,时间复杂度分析和求逆类似,是 $O(n\log n)$。 4. **形式幂级数幂函数:** 给定一个幂级数 $G(x)\bmod x^n$ 和实数 $k$,求出一个幂级数 $F(x)\bmod x^n$ 使得 $F(x)\equiv G^k(x)\bmod x^n$。 思路一:倍增快速幂。 当 $k\in \Z^+$ 时,我们只要用倍增快速幂,每次计算一次多项式乘法即可,时间复杂度为 $O(n\log^2 n)$。 思路二:转化为 $\ln$ 和 $\exp$。 将要求的式子两边 $\ln$,得到: $$ \ln(F(x))\equiv k\ln(G(x))\bmod x^n $$ 再 $\exp$ 回去,就得到: $$ F(x)\equiv \exp(k\ln(G(x)))\bmod x^n $$ 所以我们做一次 $\ln$ 和一次 $\exp$ 即可。 但是事情还没完,我们之前说过能做 $\ln$ 的条件是常数项为 $1$,如果常数项不为 $1$,就不能直接这样做。 当然这个也很好处理,设 $a_ix^i$ 是 $G(x)$ 中的最低非零项,那么 $\dfrac{G(x)}{a_ix^i}$ 就是一个常数项为 $1$ 的幂级数了,我们将上面的式子稍作改写: $$ F(x)\equiv (a_ix^i)^k\times \exp(k\ln(\dfrac{G(x)}{a_ix^i}))\bmod x^n $$ 这就解决了常数项不一定为 $1$ 的问题。 这个做法的时间复杂度为 $O(n\log n)$。 至此,我们已经学会了对于 $\bmod x^n$ 意义下的幂级数: - 在 $O(n)$ 的时间复杂度内计算**加减导积**; - 在 $O(n\log n)$ 的时间复杂度内计算**乘除幂指对**。 由于三角函数和反三角函数目前没有发现什么好的用途,所以这里不再具体介绍了,介绍这方面的文章在网上也能找到一些。 下面介绍一个多项式开平方的 trick(因为最常见的幂函数其实是开平方),这个做法可以做到比直接计算幂函数更小的常数: 考虑倍增的迭代过程,我们要求的是 $F(x)\equiv \sqrt {G(x)}\bmod x^n$,已知: $$ F_0(x)\equiv \sqrt {G(x)}\bmod x^{\frac{n}{2}} $$ 利用在求逆部分介绍的平方法或者直接带牛顿迭代的公式,我们可以得到(这里以平方法为例): $$ \begin{aligned} (F(x)-F_0(x))^2 & \equiv 0\bmod x^n \\ F^2(x)-2F(x)F_0(x)+F_0^2(x) & \equiv 0\bmod x^n \\ G(x)-2F(x)F_0(x)+F_0^2(x) & \equiv 0\bmod x^n \\ F(x) & \equiv \dfrac{G(x)+F_0^2(x)}{2F_0(x)}\bmod x^n \end{aligned} $$ 然后按照这个式子迭代,实际效果应该优于 $\exp+\ln$ 的做法。 --- 至此,我们掌握了形式幂级数的基本理论和基本运算。 这些运算将会用于之后提到的很多组合问题。 ## 四、生成函数基础 --- 这一部分将会介绍:一般型生成函数和指数型生成函数的组合意义,生成函数求解数列,若干计数问题例子。 ### (一)概念 对于数列 $f_n\ (n\in \mathbb{N})$,定义其 **一般型生成函数(OGF)** 为: $$ F(x)=\sum\limits_{i=0}^{\infty}f_ix^i $$ 定义其 **指数型生成函数(EGF)** 为: $$ F(x)=\sum\limits_{i=0}^{\infty}\dfrac{f_i}{i!}x^i $$ 二者统称为该数列的生成函数,它们都是形式幂级数。 生成函数是描述一个数列的方式之一,是解决数列问题的有用工具。 对数列的操作和变化,以及对数列的研究,可以用生成函数的运算和性质来刻画。 ### (二)组合意义 记两个数列 $f_n\ (n\in \mathbb{N}),\ g_n\ (n\in \mathbb{N})$ 的 OGF 为 $F(x),\ G(x)$,其中 $f_i$ 表示:**用 A 方法选出 $i$ 个元素的方案数**,$g_i$ 则表示:**用 B 方法选出 $i$ 个元素的方案数**。 考虑一个新数列 $h_n\ (n\in \N)$,其 OGF 满足 $H(x)=F(x)\times G(x)$,考虑 $h_i$ 的组合意义: 根据 OGF 的定义我们知道: $$ h_n=[x^n]H(x)=\sum\limits_{i=0}^n[x^i]F(x)\times [x^{n-i}]G(x)=\sum\limits_{i=0}^nf_ig_{n-i} $$ 而最后的 $f_ig_{n-i}$ 可以理解为:用 A 方法选出 $i$ 个元素,再用 B 方法选出 $n-i$ 个元素的方案数,再加上枚举 $i$,相当于就是:总共选出 $n$ 个元素的方案数,无论分别用 A,B 方法选了几个。 注意这里的元素是无标号的,也就是我们不考虑 $n$ 个元素排列的顺序。 所以我们可知,两个数列 OGF 相乘的组合意义是:**用两种方式共选出某一数量的无标号元素(的方案数)**。 接下来再考虑两个数列的 EGF,也记作 $F(x),G(x)$。 考虑一个新数列 $h_n\ (n\in \mathbb{N})$,其 EGF 满足 $H(x)=F(x)\times G(x)$,考虑 $h_i$ 的组合意义。 先写出 $h_n$ 的表达式: $$ h_n=n!\times [x^n]H(x)=n!\sum\limits_{i=0}^n[x^i]F(x)\times[x^{n-i}]G(x)=n!\sum\limits_{i=0}^n\dfrac{f_ig_{n-i}}{i!(n-i)!} $$ 而如果我们将 $n!$ 与 $i!(n-i)!$ 组合,发现这是一个二项式系数,所以: $$ h_n=\sum\limits_{i=0}^n\binom n i f_ig_{n-i} $$ 这个式子称为 **二项卷积**,其组合意义可以理解为:在 $n$ 个元素中,选出 $i$ 个用 A 方案选,另外 $n-i$ 个用 B 方案选,但和 OGF 不同的是,我们发现这里的元素是有标号的,也就是我们需要考虑用 A 方案选的元素和用 B 方案选的元素的先后次序。 所以我们可知,两个数列 EGF 相乘的组合意义是:**用两种方式共选出某一数量的带标号元素(的方案数)**。 那么如果是两个相同的生成函数相乘,又有什么特殊的组合意义呢?考虑 $F^2(x)$ 的组合意义,以同一方式取两次,共取走了 $n$ 个元素,我们将这个二次推广到 $n$ 次可知:$F^n(x)$ 的组合意义就是以统一方式共取 $n$ 次元素,总共取到某一数量元素的方案数,有无标号根据 EGF 还是 OGF 来决定。 注意指数上这个“取的次数”是带标号的,例如第一次取一个,第二次取两个和第一次取两个,第二次取一个是不同的取法。 ## 下面划掉的这一段是胡说八道,我之后会改掉。 ~~然而如果我们需要让这个取的过程无标号,就需要用到另一种运算:$\exp$。~~ ~~设一般型生成函数 $F(x)=\sum\limits_{i=0}^{\infty}f_ix^i$,其中 $f_0=0$,另一个一般型生成函数 $G(x)=\exp(F(x))$,那么我们按照 $\exp$ 的麦克劳林公式展开:~~ $$ G(x)=\sum\limits_{i=0}^{\infty}\dfrac{F^i(x)}{i!} $$ ~~而 $\dfrac{F^i(x)}{i!}$ 正是在 $F^i(x)$ 的同时去除了 $i$ 次取之间的顺序关系,也就是:将 $n$ 分成 $i$ 个无标号部分,每个部分都用 $f_i$ 的方式取一次,而再加上枚举 $i$,我们可知:~~ ~~对 OGF 进行 $\exp$ 的组合意义:**以某种方式取任意多次无标号元素,共取到 $n$ 个元素的方案数**。~~ ~~同理,我们直接给出 EGF 的 $\exp$ 的组合意义:**以某种方式取任意多次带标号元素,共取到 $n$ 个元素的方案数**。~~ 而对于 $\ln$,我们就将其理解为 $\exp$ 的逆运算,也就是:**已知以某种方式取任意多次共取到 $n$ 个元素,每一次取到一定数目元素(的方案数)**。 一些其他运算的意义在遇到之后再说(如开方,一般在解一元二次方程中遇到)。 --- 实例:用生成函数求卡特兰数 $C_n$ 的通项公式。 卡特兰数 $C_n$ 是指:长度为 $2n$ 的由 $n$ 个 $0$ 和 $n$ 个 $1$ 组成的,且满足任意前缀中 $0$ 的个数不少于 $1$ 的个数的序列数量。 首先我们给出一条卡特兰数的递推式: $$ \begin{aligned} & C_0=1 \\ & C_n=\sum\limits_{i=0}^{n-1}C_iC_{n-1-i}\quad(n\in \mathbb{Z}^+)\\ \end{aligned} $$ 这个式子的解释是:考虑最小的 $i$ 使得前 $2i$ 个数中 $0,1$ 同样多(都有 $i$ 个),那么对于区间 $[2,2i-1]$,这个区间中每个前缀中 $0$ 的个数也不能少于 $1$ 的个数(否则就存在更小的 $i$ 了),且 $0,1$ 同样多,所以这一段的方案数是 $C_{i-1}$,之后的 $[2i+1,2n]$ 可以理解为另一个独立的部分,方案数是 $C_{n-i}$,所以总共就是 $C_{i-1}C_{n-i}$,记 $i'=i-1$ 即得到上面的等式。 根据这个式子,我们来导出卡特兰数的一般型生成函数 $F(x)$ 的性质: $$ F(x)=1+xF^2(x) $$ 我们对比两边系数:$[x^0]F(x)=1$,而对于非零次项系数,就是上面的递推式,说明该等式成立。 这是一个关于 $F(x)$ 的一元二次方程,我们根据求根公式解这个方程: $$ \begin{aligned} & xF^2(x)-F(x)+1=0 \\ & F(x)=\dfrac{1\pm \sqrt{1-4x}}{2x} \end{aligned} $$ 这时候我们发现麻烦:这个方程有两解,我们要知道哪个是我们需要的。 这里有两种常用方法: 1. 常数项对比:首先将 $F(x)$ 进行分子有理化: $$ F(x)=\dfrac{1\pm \sqrt{1-4x}}{2x}=\dfrac{2}{\mp\sqrt {1-4x}-1} $$ 如果取 $+$,那么我们取 $x=0$,得到 $F(x)=\dfrac{2}{-2}=-1$,而 $F(0)=[x^0]F(x)=C_0=1$,矛盾。 反之,如果取 $-$,那么当 $x\to 0$ 时(注意这里不能 $x=0$,否则分母为 $0$),用洛必达法则暴力计算验证一下(这里只能用那个分母为 $2x$ 的式子算,因为中间的变换约分了 $x$): $$ \lim\limits_{x\to 0}F(x)=\dfrac{(1-\sqrt {1-4x})'}{(2x)'}=\dfrac{-\frac{(1-4\times 0)^{-0.5}}{2}\times (-4)}{2}=1 $$ 注意中间运用了链式法则。 2. 取未定式而非无穷大:取 $x=0$,如果取 $+$ 的话则是 $\dfrac{c}{0}\ (c>0)$,这种时候我们取 $\dfrac{0}{0}$ 形式的那个而不取 $\dfrac{c}{0}$ 形式的那个,所以取 $-$。 如果只是要求某一范围内的卡特兰数,到这里已经结束了,我们可以用形式幂级数开方、求逆的方法算出(这里 $[x^0]G(x)=0$ 时的除法可以类比幂函数求法,提出最低次项来做,或者直接使用分子有理化后的式子做)。 但如果要得到卡特兰数的通项公式,那么我们进一步推导。 根据广义二项式定理,我们有: $$ \sqrt {1-4x}=\sum\limits_{i=0}^{\infty}\binom {0.5}{i}1^{0.5-i}(-4x)^i $$ 其中对二项式系数进行展开: $$ \binom {0.5}{i}=\dfrac{0.5^{\underline i}}{i!}=\dfrac{(-1)^i\prod\limits_{j=1}^i(2j-3)}{2^i\times i!}=-\dfrac{(-1)^i\prod\limits_{j=1}^{i-1}(2j-1)}{2^i\times i!} $$ 其中的双阶乘继续化简: $$ \prod\limits_{j=1}^{i-1}(2j-1)=\dfrac{\prod\limits_{j=1}^{2i-2}j}{\prod\limits_{j=1}^{i-1}2j}=\dfrac{(2i-2)!}{2^{i-1}(i-1)!} $$ 带入二项式系数: $$ \binom {0.5}{i}=-\dfrac{(-1)^i}{2^i}\times \dfrac{(2i-2)!}{i!(i-1)!}=-\dfrac{(-1)^i}{2^{2i-1}\times i}\binom {2i-2}{i-1} $$ 进一步带入原式: $$ \sqrt {1-4x}=1+\sum\limits_{i=1}^{\infty}-\dfrac{(-1)^i}{2^{2i-1}\times i}\times \binom {2i-2}{i-1} \times (-4)^{i}\times x^i=1+\sum\limits_{i=1}^{\infty}-\dfrac{2}{i}\binom {2i-2}{i-1}x^i $$ 再次带入最前面的式子: $$ F(x)=\dfrac{1-\Big(1-\sum\limits_{i=1}^{\infty}\dfrac{2}{i}\binom {2i-2}{i-1}x^i\Big)}{2x}=\sum\limits_{i=0}^{\infty}\dfrac{\binom {2i}{i}}{i+1}x^i $$ 这里已经很清晰了,对比系数即可得到:$C_n=\dfrac{\binom {2i}{i}}{i+1}$,于是我们得到了卡特兰数的通项公式。 这个例子里运用了一些具体的对生成函数进行运算的例子,可以发现生成函数在数列问题当中的重要运用。 --- ### 例题 $1$:[食物](https://darkbzoj.tk/problem/3028) **题目大意:** 问选择以下八种食品共 $n$ 个的方案数对 $10007$ 取模的结果,有如下要求: 承德汉堡:偶数个; 可乐:$0$ 个或 $1$ 个; 鸡腿:$0$ 个,$1$ 个或 $2$ 个; 蜜桃多:奇数个; 鸡块:$4$ 的倍数个; 包子:$0$ 个,$1$ 个,$2$ 个或 $3$ 个; 土豆片炒肉:不超过一个; 面包:$3$ 的倍数个。 $n\leq 10^{500}$。 **解题思路:** 根据 OGF 乘法的组合意义,考虑构造每种食品的 OGF,相乘后取 $x^n$ 项系数即为答案。 承德汉堡:$1+x^2+x^4+\ldots=\dfrac{1}{1-x^2}$; 可乐:$1+x$; 鸡腿:$1+x+x^2$; 蜜桃多:$x+x^3+x^5+\ldots=\dfrac{x}{1-x^2}$; 鸡块:$1+x^4+x^8+\ldots=\dfrac{1}{1-x^4}$; 包子:$1+x+x^2+x^3$; 土豆片炒肉:$1+x$; 面包:$1+x^3+x^6+\ldots=\dfrac{1}{1-x^3}$; 相乘后得到:$F(x)=\dfrac{x}{(1-x)^4}=x\times \Big(\dfrac{1}{1-x}\Big)^4$,我们需要 $[x^n]F(x)$ 即 $[x^{n-1}]\Big(\dfrac{1}{1-x}\Big)^4$。 发现 $\dfrac{1}{1-x}$ 的级数形式就是 $1+x+x^2+\ldots$,所以 $[x^{n-1}]\Big(\dfrac{1}{1-x}\Big)^4$ 的组合意义就是选择四个数相加等于 $n-1$ 的方案数,众所周知这等于 $\binom {n+2}{3}$,所以答案就是 $\binom {n+2}{3}$。 根据 Lucas 定理,$\binom {n+2}{3}\equiv \binom {\frac{n+2}{p}}{\frac{3}{p}}\times \binom {(n+2)\bmod p}{3\bmod p}\equiv \binom {(n+2)\bmod p}{3}\bmod p$,所以读入 $n$ 时直接边读边对 $p$ 取模即可。 --- ### 未完待续