离谱的多项式
——简单多项式和生成函数学习笔记
By Tuifei_oier
Part 1 前言
多项式和生成函数同属 OI 中数学方面的知识,在学习它们的过程中,不仅要掌握一些基础的对于它们本身的知识和操作,更要学会去通过构造和计算来解决相关的问题。
因此,本文先从定义入手记录一些多项式和生成函数的基础操作,再记录一些经典的应用和构造方式。
Part 2 科技篇
随便编的定义
首先,我们给出形式幂级数的定义:
对于形式幂级数 (F(x)=sumlimits_{ige0}a_ix^i),我们不关心 (x) 的取值,也不关心它的收敛性等,仅仅关心它的各项系数。因此,对于形式幂级数,我们认为它可以进行任何变换而不受限制。例如:
一般来说,当 (xin(0,1)) 时左式收敛,于是等号成立。
但是对于形式幂级数,由于我们不关心 (x) 的值也不关心求和式最终的结果,所以等号何时都成立。
接下来,定义接下来讨论的多项式 (F(x)=sumlimits_{i=0}^na_ix^i),它是一个 (n) 次形式多项式,即系数数列有限项的形式幂级数。在 OI 中,我们大部分情况下讨论形式多项式,即我们同样不关心 (x) 的具体取值。
一些表示:
(F(x_0)) 表示 (x=x_0) 时 (F(x)) 的取值。
(F_i) 或 ([x^i]F(x)) 表示 (F(x)) 的 (x^i) 项的系数。
多项式全家桶
首先是最基础的卷积,即:
就是把两个多项式相乘,利用 FFT 可以 (O(nlog n)) 解决。
一维循环卷积,即:
发现实际上 FFT 求的就是循环卷积,于是直接做之后把系数加到 (mod n) 后的位置上就好了。
对于二维循环卷积,即:
我们可以先求出对于两边的矩阵做 DFT,然后对位相乘得到新矩阵再 IDFT 回去。
如何 DFT 一个矩阵呢?我们可以先把每一行看成一个多项式系数数列,然后把它们代表的多项式都 DFT,对得到的新矩阵的每一列再进行相同操作,最后就得到了原矩阵的 DFT 矩阵,IDFT 就是把整个操作逆向进行即可。
对于高维,同理。
关于循环卷积有一个重要的算法——Bluestein 算法。
因为在 DFT 和 IDFT 过程中都要求 (n) 为 (2) 的若干次方;而当 (n) 不为 (2) 的次方时,我们要不一次一次暴力做,但这样可能会导致复杂度问题;或者,我们就可以利用这个算法。具体的说,对于长度为 (n) 的数列 (A),我们要求长度为 (n) 的数列 (B) 满足:
于是,我们设 (C_i=x^{frac{i^2}{2}}sumlimits_{j=0}^{i}A_jx^{frac{j^2}{2}}x^{frac{-(i-j-n)^2}{2}}),此时,由于 (forall xge n,A_x=0),所以 (B_i=C_{i+n}),而 (C_i) 就是一个卷积形式的式子了,我们就可以 (O(nlog n)) 求对于任意的 (n) 的 DFT 和 IDFT 了。
多项式求逆,即给定 (n-1) 次多项式 (F(x)),求次数不超过 (n-1) 的多项式 (G(x)) 满足:
如何求这个东西呢?我们考虑用倍增来求。
假设我们当前已经知道 (G_n(x)) 使得 (F(x)\,G_n(x)equiv1(mod x^n)),要求 (G_{2n}(x)) 使得 (F(x)\,G_{2n}(x)equiv1(mod x^{2n}))。
则:
于是我们就可以倍增了,复杂度 (O(nlog n))。
多项式除法,即给定 (n) 次多项式 (F(x)) 和 (m) 次多项式 (G(x)),求一个 (n-m) 次多项式 (H(x)) 和一个次数小于 (m) 的多项式 (R(x)),使得:
如何求呢?我们考虑设 (F'(x)=F(dfrac{1}{x})x^n),即 (F(x)) 的 (0-n) 次项系数翻转,则(假设 (R(x)) 次数为 (m-1),不够则补 (0)):
而 (H'(x)) 的次数为 (n-m),所以我们在 (mod x^{n-m+1}) 意义下求出的 (H'(x)) 和真实的 (H'(x)) 相同。
于是通过多项式求逆求出 (H'(x)),代回原式解出 (R(x))。
复杂度 (O(nlog n))。
多项式 ln,即给定多项式 (F(x)),求 (ln F(x)pmod{x^n})。
首先解释下为什么一个多项式可以 (ln)。从这里开始由于我们把多项式看成形式多项式,因此在把 (f(x)=ln x) 在 (x_0=1) 处泰勒展开后得到的式子恰为一个关于 (x) 的幂级数,把 (x) 用 (F(x)) 来代入就可以理解为 (ln F(x))。
接下来考虑怎么计算。
令 (G(x)=ln F(x)pmod{x^n}),则:
于是求个逆求个导卷起来再积分回去即可。
常数项肯定为 (0)。(因为 (F(x)) 的常数项肯定为 (1))
牛顿迭代。即对于一个函数(自变量任意)(G(x)),求多项式 (F(x)) 使得 (G(F(x))equiv0pmod{x^n})。
考虑倍增。假设我们知道 (G(F_n(x))equiv0pmod{x^n}),欲求 (F_{2n}(x)) 满足 (G(F_{2n}(x))equiv0pmod{x^{2n}})。
把 (G(x)) 在 (G(F_n(x))) 处作泰勒展开,得到:
代入 (x=F_{2n}(x)),则
注意到 (F_{2n}(x)) 与 (F_n(x)) 的前 (n-1) 项应该相同,则 (F_{2n}(x)-F_n(x)equiv0pmod{x^n}),于是上式在 (pmod{x^{2n}}) 意义下为:
这样就可以倍增了。之前的一些科技也可以用这种牛顿迭代的形式来求(如求逆)。
多项式 exp,即给定 (P(x)),求 (e^{P(x)}pmod{x^n})。
如何理解?(f(x)=e^x) 同样可以泰勒展开成幂级数。
设函数 (G(x)=ln x-P(x)),利用刚才的牛顿迭代,得到:
写完 (ln) 后倍增即可,复杂度 (O(nlog n)),注意常数项为 (1)。
多项式快速幂,即给定 (F(x)),求 (G(x)equiv F^k(x)pmod{x^n})。
首先可以和普通快速幂一样做,复杂度 (O(nlog^2n))。
考虑低一点的复杂度,我们先把 (F^(x)) 做 (ln),给系数都乘上 (k) 之后再 (exp) 回去。
但有一点问题,就是 (ln) 和 (exp) 均对常数项有要求。如何解决?提公因式即可,注意 (k) 的细节。
复杂度 (O(nlog n))。
生成函数
一般来说,对于数列 ({a_i}),我们有两种生成函数:
- OGF,一般生成函数,形如 (F(x)=sumlimits_{ige0}a_ix^i);
- EGF,指数生成函数,形如 (F(x)=sumlimits_{ige0}dfrac{a_i}{i!}x^i)。
先看一般生成函数,我们把一个数列写成这样的形式,就可以实现这样的效果:
小例题:给定 (4) 种物品,每种物品都有无限个,其中,第一种物品只能选偶数个,第二种只能选 (5) 的倍数个,第三种物品最多选 (4) 个,第四种物品,最多选 (1) 个,求从中选出 (n) 个物品的方案数。
考虑写出四种物品的拿取方案序列:
({1,0,1,0,...},{1,0,0,0,0,1,0,0,0,0,1,...},{1,1,1,1,1,0,0...},{1,1,0,0,...})
即序列第 (i) 项(从 (0) 开始)表示对于该种物品拿 (i) 个的方案数。
求出它们的生成函数,把它们乘在一起:
此时,所得多项式第 (n) 项的系数即为,答案,不难得到答案为 (n+1)。
这就是一般生成函数的妙用,两个生成函数的卷积可以理解为在做背包合并问题,方便计数。
同时,假设我们要求一个数列的若干项,可以先写出这个数列的生成函数,然后通过某些计算算出这个生成函数的各项系数(比如利用多项式科技),这样一来就解决了问题。
再看指数生成函数,为什么写成这样呢?考虑两个指数生成函数 (F(x),G(x)) 乘积:
这样一来,乘在一起后系数不是 (a_jb_{i-j}),而是还有一个组合数 (C_i^j),就可以解决一些排列组合相关的问题。
Part 3 应用篇
接下来是一些上文的应用。
实际上在 OI 中的应用还是以生成函数的形式为主,但也有利用卷积定义之类的题型。
Pro A 第二类斯特林数 (Luogu 5395)
给定 (n,m),求 (S(n,0),S(n,1),S(n,2),...,S(n,m)) 的值。
(tips:1le mle nle 10^5)。
直接暴力算肯定不行,考虑如何优化。
我们发现,把斯特林数写成通项形式:
于是,构造 (f(x)=sumlimits_{i=0}^mdfrac{(-1)^i}{i!}x^i,g(x)=sumlimits_{i=0}^mdfrac{i^n}{i!}x^i),则 (S(n,m)=[x^m](f*g))。
这就是一个经典的应用卷积定义的算法,复杂度 (O(nlog n))。
Pro B 付公主的背包(Luogu 4389)
给定 (n) 个物品,每种物品的都有无数个,物品 (i) 的体积为 (v_i)。
求挑出若干个物品使得体积和为 (V) 的方案数。
(tips:1le n,Vle 10^5)。
整个问题就是一个背包问题,考虑用一般生成函数来求方案数。
则第 (i) 个物品的生成函数 (f_i(x)=sumlimits_{jge0}x^{v_ij}=dfrac{1}{1-x^{v_i}})。
于是我们要求答案数列的生成函数
再积回去,得到:
于是求出 (ln G(x)),再多项式 (exp) 即可。
复杂度 (O(Vlog V)),注意在求 (ln G(x)) 时需要用到一些小技巧。
Pro C 排列数 (Luogu 5824)
给定 (n,m),求 (p_{n,m}) 的值。
(tips:1le n,mle2 imes10^5),其中 (p_{x,y}) 表示将 (x) 写成 (y) 个自然数的和的方案数,不考虑顺序。
例如,(5=(0+0+5)=(0+1+4)=(0+2+3)=(1+1+3)=(1+2+2)),因此 (p_{5,3} = 5)。
首先,我们有一个 (O(nm)) 的递推:
考虑为什么,我们对于 (p_{i,j}),分别考虑 (j) 个自然数中是否存在 (0) 来转移。
但这道题仅仅这样是不行的,考虑优化。
我们设 (F_i(x)=sum_{jge0}p_{j,i}x^j),则:
于是移项得到:
而 (dp_{i,0}=[i=0]),于是 (F_0(x)=1),所以:
发现了什么?这玩意跟上一题的式子几乎一模一样。
于是直接一样的处理就行了,复杂度 (O(nlog n))。
这道题属于一类经典问题,通过构造答案数列的生成函数,利用递推式等关系来得到生成函数的关系并解方程,最后就可以得到答案了。
Pro D 数学竞赛 (Luogu 5517)
给定一个长度为 (2^{64}) 的数列 ({a_n}),其中 (a_0=-3,a_1=-6,a_2=-12,a_n=3a_{n-1}+a_{n-2}-3a_{n-3}+3^n)。
(T) 组询问,每次给定 (n),求 (a_npmod{p=10^9+7})。
(tips:1le Tle5 imes10^7,0le nle2^{64}-1)。
介绍一种比较生成函数的做法:
考虑利用刚才的想法,设 (F(x)=sum_{ige0}a_ix^i),则:
解方程得到:
此时,对于这种分母为乘积的形式,一种比较通用的方法是待定系数法,即:
此时,(A,B,C,D) 可以解出来(参见原题解),然后就把它展开成幂级数形式,最后就得到答案了。
复杂度可以做到 (O(sqrt p-T))。
Pro E 贝尔数 (Luogu 5748)
定义贝尔数 (B_i=sumlimits_{j=0}^iS(i,j)),(T) 组询问,求 (B_n)。
(tips:1le Tle1000,1le nle10^5)。
考虑贝尔数 (B_n) 的组合意义,等于是把 (n) 个带放入 (n) 个无标号的可空集合中的方案数。
既然要利用上面的想法,我们就应该尝试去找贝尔数的递推公式,发现:
即枚举和 (n) 在一个集合的是哪些数。
发现相当于是做卷积的过程中增加了一项系数 (inom{n}{j}),这启示我们使用指数生成函数。
设 (F(x)=sumlimits_{jge0}dfrac{B_j}{j!}x^j,F'(x)=sumlimits_{jge0}dfrac{B_{j+1}}{j!}x^j),于是 (F'(x)=F(x)e^x)。
两边求导移项后得到:
代入 (B(0)=1) 得到 (c=-1),于是 (B(x)=e^{e^x-1})。
复杂度 (O(nlog n-T))。
Part 4 小结
生成函数和多项式作为近些年新兴的出题方向,都透露了极强的数学性,要求 oier 有较高的数学计算能力和思维力,同时又可以方便地结合类似递推之类的内容,因此有很有必要好好掌握。
通过这样的小结形式,积累下来的 trick 或许会有大用吧。