本文将介绍如何 O(n2) 计算多项式求逆、取 exp、取 ln。
Q:为什么不用基于 FFT / NTT 的 O(nlogn) 方法?
A:
- 自从快速傅里叶变换(FFT)在 NOI 大纲中被定为 10 级(相当于移除考纲)后,多项式技术开始式微,大多数生成函数问题不需要优化到 O(n⋅polylog(n)),而只需要做到 O(n2)。因此没有必要记忆复杂的线性对数算法。
- 我们有时会对很小的多项式进行非常多次的操作(例如集合幂级数),此时 O(n2) 的方法无论是实现难度还是速度都远远胜过 O(nlogn) 的方法。
- 题目给出的模数可能无法支持 NTT 操作。
为了方便,令 Fi=[zi]F(z)。
由于这玩意我总是忘记,所以记一下,后面会用到:
(f(g(z)))′=f′(g(z))⋅g′(z)
给定多项式 F(z),令 G(z)=F(z)1,求 G(z)modxn。保证 F0=0。
根据定义有:
[zk](F(z)⋅G(z))=i=0∑kFiGk−i=[k=0]
当 k=0 时 F0G0=1⟹G0=F0−1。因此接下来可令 k>0:
i=0∑kFiGk−i=0⟹−F0Gk=i=1∑kFiGk−i⟹Gk=−F01i=1∑kFiGk−i
可以直接递推,时间复杂度为 O(n2)。
给定多项式 F(z),令 G(z)=expF(z),求 G(z)modxn。保证 F0=0。
根据定义有:
G′(z)=exp(F(z))⋅F′(z)=G(z)⋅F′(z)⟹[zk]G′(z)=(k+1)Gk+1=i=0∑k[zi]F′(z)⋅Gk−i=i=0∑k(i+1)Fi+1Gk−i⟹Gk=k1i=0∑k−1Gk−1−iFi+1(i+1)=k1i=1∑kGk−iFii
边界 G0=0。可以直接递推,时间复杂度为 O(n2)。
给定多项式 F(z),令 G(z)=lnF(z),求 G(z)modxn。保证 F0=1。
有两种做法。
根据定义有:
G′(z)=(lnF(z))′=F(z)1⋅F′(z)⟹G(z)=∫F(z)1⋅F′(z)dz
多项式求导和多项式积分是容易的,复杂度瓶颈在多项式求逆和多项式乘法。我们全部用 O(n2) 的算法就可以做到 O(n2)。
这个方法易于理解但常数较大。
根据定义有:
G′(z)=F(z)1⋅F′(z)⟹F′(z)−F(z)G′(z)=0⟹[zk]F′(z)=(k+1)Fk+1=i=0∑kFk−i(i+1)Gi+1⟹Fk+1=k+11i=0∑kFk−i(i+1)Gi+1⟹Fk=k1i=0∑k−1Fk−i−1(i+1)Gi+1⟹Fk=k1i=1∑kFk−iiGi=k1i=1∑k−1Fk−iiGi+Gk⟹Gk=Fk−k1i=1∑k−1Fk−iiGi
边界 G0=0。可以直接递推,时间复杂度为 O(n2)。
给定多项式 F(z),令 G(z)=F(z),求 G(z)modxn。保证 F0=0。
G(z)=F(z)=explnF(z)=exp21lnF(z)
只需要一次 ln + 一次 exp 就可以计算,需要适当调整以满足常数项限制。
时间复杂度为 O(n2),常数较大。
根据定义有(显然有边界 G0=F0):
G2(z)=F(z)⟹Fk=i=0∑kGiGk−i=2F0Gk+i=1∑k−1GiGk−i⟹Gk=2F0Fk−2F01i=1∑k−1GiGk−i
可以直接递推,时间复杂度为 O(n2)。
操作 | 递推公式 | 边界 | 隐含限制 |
---|
求逆 | Gk=−F01∑i=1kFiGk−i | G0=F01 | F0=0 |
exp | Gk=k1∑i=1kGk−iFii | G0=1 | F0=0 |
ln | Gk=Fk−k1∑i=1k−1Fk−iiGi | G0=0 | F0=1 |
开根 | Gk=2F0Fk−2F01∑i=1k−1GiGk−i | G0=F0 | F0=0 |