前言

搞完多项式乘法之后,其实我就想把多项式的这一堆都做了,但是鸽了很久都没动。。
最近正在一点点啃,笔记都会记在这里。
To do list:
多项式快速插值 \text{Interpolation}Interpolation

\text{Part1 万恶之源——多项式乘法}Part1 万恶之源——多项式乘法
在我的这篇blog里,较为详细地讲了FFT,借由这个算法,可以实现\Theta(n\log n)Θ(nlogn)的多项式乘法(卷积)。
可是FFT要用到复数,这使得精度没办法保障,而且还不能取模。
那有没有别的方法解决这个问题呢?
答案很自然地是肯定的。

在数论中,有一个叫做原根的东西。简单来说,质数pp的原根定义是这样的:
若整数g^i\mod pgimodp的结果两两不同,且g\in[2,p-1]g[2,p1],i\in[1,p-1]i[1,p1],那么gg就是pp的原根。
比如质数998244353998244353的原根就是33

这个原根的用处就在于,它有着和FFT中的单位根\omegaω相似的性质!
利用这个性质我们就可以实现快速数论变换NTT(\text{Number Theory Transform}Number Theory Transform)
在那篇FFT的代码中,有一行:

complex rt = complex(cos(pi/mid),type*sin(pi/mid));

其中\text{type}=1type=1时,单位根为e^\frac{i\pi}{mid}emidiπ
而在NTT中,“单位根”就成了:g^{\frac{p-1}{2mid}}g2midp1,这里的gg就是原根。
一般我们取p=998244353,g=3p=998244353,g=3,方便计算。
然后我们就能打出NTT的板子了,和FFT极为相近:

void NTT(int *a,int type,int lim){
    for(int i=0;i<=lim;++i){
        if(i>=rev[i]) continue;
        swap(a[i],a[rev[i]]); //这里的rev同FFT中的翻转数组
    }
    for(int mid=1;mid<lim;mid<<=1){
        int rt = power(3,(p-1)/(mid<<1)); //上述的单位根计算方法
        if(type==-1) rt = power(rt,p-2); //如果是逆变换,单位根要变逆元
        int r = mid<<1;
        for(int j=0;j<lim;j+=r){
            int w = 1;
            for(int k=0;k<mid;++k){
                int x = a[j+k];
                int y = (ll)w*a[j+k+mid]%p;
                a[j+k] = (x+y)%p;
                a[j+k+mid] = ((x-y)%p+p)%p;
                w = (ll)w*rt%p;
            }
        }
    }
}

用NTT计算多项式乘法,和FFT一样,最后出来的系数要除以\limlim (也就是乘\limlim在模pp下的逆元)
ps:用NTT比FFT常数小了不少呢!
\text{Part2 强力工具——多项式求逆}Part2 强力工具——多项式求逆
在做有关多项式的题时,我们经常遇到这样的问题:
给定一个n-1n1次多项式F(x)F(x)求一个多项式G(x)G(x),使其满足:
G(x)F(x)\equiv1\space (\text{mod }x^n)G(x)F(x)1 (mod xn)
这里模x^nxn的意义就在于,忽略次数\ge nn的项。

这个东西好像没法直接求,我们设一个多项式G_1(x)G1(x),满足条件:
G_1(x)F(x)\equiv1\space (\text{mod }x^{\lceil\frac{n}{2}\rceil})G1(x)F(x)1 (mod x2n)
和刚才那个式子做差,可以得到:
F(x)(G(x)-G_1(x))\equiv0\space (\text{mod }x^{\lceil\frac{n}{2}\rceil})F(x)(G(x)G1(x))0 (mod x2n)
稍微化简一下,也就是
G(x)-G_1(x)\equiv0\space (\text{mod }x^{\lceil\frac{n}{2}\rceil})G(x)G1(x)0 (mod x2n)将这个式子两遍同时平方:
G^2(x)-2G_1(x)G(x)+G_1^2(x)\equiv0\space (\text{mod }x^{\lceil\frac{n}{2}\rceil})G2(x)2G1(x)G(x)+G12(x)0 (mod x2n)
可以证明多项式长度翻倍上式依然成立:
G^2(x)-2G_1(x)G(x)+G_1^2(x)\equiv0\space (\text{mod }x^n)G2(x)2G1(x)G(x)+G12(x)0 (mod xn)

我们再把式子两遍同时乘 F(x)F(x),由于G(x)F(x)\equiv1\space (\text{mod }x^n)G(x)F(x)1 (mod xn),所以可以化简很多:
G(x)-2G_1(x)+F(x)G_1^2(x)\equiv 0\space (\text{mod }x^n)G(x)2G1(x)+F(x)G12(x)0 (mod xn)
再移项一下,就得到了最终的结果
G(x)\equiv 2G_1(x)-F(x)G_1^2(x)\space (\text{mod }x^n)G(x)2G1(x)F(x)G12(x) (mod xn)
这个东西,我们可以考虑倍增或者递归地来求。
因为当F(x)F(x)只有常数项的时候,它的逆也就是常数项的逆元。

这个东西的时间复杂度看起来好像不太对,我们来分析一下:
倍增过程中,每次在模 x^1,x^2,x^4...x1,x2,x4... 下求逆,也就是说多项式乘法每次也只用算前 1,2,4...1,2,4... 项,总时间复杂度就是:
T(n) = T(n/2)+\Theta(n\log n)T(n)=T(n/2)+Θ(nlogn)

\text{Part3 ——多项式对数}Part3 ——多项式对数
这个相对简单一些,我们要求一个 G(x)G(x),满足:G(x)\equiv \ln F(x)\space (\text{mod }x^n)G(x)lnF(x) (mod xn)
\ln F(x)lnF(x)没办法直接求,不过我们可以考虑先求个导
(\ln F(x))'=\frac{F'(x)}{F(x)}(lnF(x))=F(x)F(x)
再把这个东西积分一下,就变成了我们想要的式子:
\ln F(x)=\int \frac{F'(x)}{F(x)}dxlnF(x)=F(x)F(x)dx
这样就可以直接计算了,需要用到一点微积分的基础知识。
这里就不再多赘述了。

\text{Part}\frac{8}{2}\text{ ——多项式除法}Part28 ——多项式除法
给一个nn次多项式F(x)F(x),mm次多项式G(x)G(x),求n-mnm次的Q(x)Q(x),和小于mm次的R(x)R(x),满足条件:
F(x)=G(x)Q(x)+R(x)F(x)=G(x)Q(x)+R(x)
我们先定义F_r(x)Fr(x)为多项式F(x)F(x)的系数翻转后的多项式,也就是设:
F(x)=\sum\limits_{i=0}^na_ix^iF(x)=i=0naixi
F_r(x)Fr(x)就是:
F_r(x)=\sum\limits_{i=0}^na_{n-i}x^iFr(x)=i=0nanixi
随便怎么推一下,我们就能得到:
F_r(x)=x^nF(x^{-1})Fr(x)=xnF(x1)
然后让我们开始正片吧!
F(x)=G(x)Q(x)+R(x)F(x)=G(x)Q(x)+R(x)
x^{-1}x1代换xx,原式仍然成立
F(x^{-1})=G(x^{-1})Q(x^{-1})+R(x^{-1})F(x1)=G(x1)Q(x1)+R(x1)两遍同乘
x^nx^nF(x^{-1})=x^mG(x^{-1})x^{n-m}Q(x)+x^{n}R(x)F_r(x)=G_r(x)Q_r(x)+x^{n}R(x)xn,式子可以变成
xnF(x1)=xmG(x1)xnmQ(x)+xnR(x)
Fr(x)=Gr(x)Qr(x)+xnR(x)

式子两边可以同时模x^{n-m+1}xnm+1,就能再化简为
F_r(x)\equiv G_r(x)Q_r(x)\space (\text{mod }x^{n-m+1})Fr(x)Gr(x)Qr(x) (mod xnm+1)
然后我们就把Q(x)Q(x)算出来了
Q_r(x)\equiv F_r(x)G_r(x)^{-1}\space({\text{mod }x^{n-m+1}})Qr(x)Fr(x)Gr(x)1 (mod xnm+1)
算出来了Q(x)Q(x),R(x)R(x)就能很快求出来
R(x)=F(x)-G(x)Q(x)R(x)=F(x)G(x)Q(x)

\text{Part5——大毒瘤 多项式指数}Part5——大毒瘤 多项式指数
这个多项式指数函数是真的可啪 QAQ
不仅难理解,还难写,我调了三天才过。。

好吧不多说了,来看题:
给一个 n-1n1 次多项式 F(x)F(x),求一个多项式 G(x)G(x),满足条件:
G(x)\equiv e^{F(x)}\space (\text{mod }x^n)G(x)eF(x) (mod xn)
做这个之前,我们先来想一下牛顿迭代。
牛顿迭代法可以用来求函数的零点,公式如下:
\large x_{n+1}=x_n-\frac{f(x_n)}{f'(x_n)}xn+1=xnf(xn)f(xn)
那这个和我们的题有啥关系呢?
莫着急,答案马上揭晓。

我们考虑将原式两边同求对数,然后移项得到
\ln G(x) - F(x) \equiv 0\space (\text{mod }x^n)lnG(x)F(x)0 (mod xn)
F(x)F(x)是给定不变的,而G(x)G(x)是我们要求的。
所以,我们可以将G(x)G(x)看做自变量,F(x)F(x)看做常数进行牛顿迭代!

关于正确性的证明,可以把它 Taylor 展开,然后发现高次项都没了。由于太麻烦这里就不写了(

于是我们就构造一个多项式H(x)H(x)
H(G(x))=\ln G(x) - F(x)H(G(x))=lnG(x)F(x)
G_i(x)Gi(x)为第ii次迭代的答案,然后套上牛顿迭代的公式
G_{i+1}(x) \equiv G_i(x)-\frac{H(G(x))}{H'(G(x))}\space (\text{mod } x^n)Gi+1(x)Gi(x)H(G(x))H(G(x)) (mod xn)
展开,再化简成
G_{i+1}(x) \equiv G_i(x)-G_i(x)(\ln G_i(x)-F(x))\space (\text{mod } x^n)Gi+1(x)Gi(x)Gi(x)(lnGi(x)F(x)) (mod xn)
G_{i+1}(x) \equiv G_i(x)(1-\ln G_i(x)+F(x))\space (\text{mod } x^n)Gi+1(x)Gi(x)(1lnGi(x)+F(x)) (mod xn)
看到这里,发现每次计算G(x)G(x)的次数都翻了一倍。
所以,如果G_{i+1}(x)Gi+1(x)是在模x^nxn下的答案的话,G_i(x)Gi(x)就是在模x^{\lceil\frac{n}{2}\rceil}x2n⌉下的答案。

于是这个玩意就可以用类似求逆的方法,倍增地来算。
边界条件为 G(x)G(x) 次数为 00,常数项为 11
至于时间复杂度的证明,也可以参考求逆的部分,这里就不再写一遍了。

\text{Part6——多项式快速幂}Part6——多项式快速幂
学过对数的都知道:
\large \ln x^n=n \ln xlnxn=nlnx
所以要计算F(x)^kF(x)k,也可以用类似的办法
\large F(x)^k=e^{k\ln F(x)}F(x)k=eklnF(x)
直接求对数,乘kk,再求指数就好了。

\text{Part}\sqrt{49}\text{ ——多项式开根}Part49 ——多项式开根
由于\sqrt n=n^{\frac12}n=n21,所以可以直接用快速幂的办法来做
可是这只适用于F(x)F(x)常数项为11的情况。

如果常数项不为11,我们就要考虑下面的方法
G(x)^2\equiv F(x)\space({\text{mod }x^n})G(x)2F(x) (mod xn)
还是考虑牛顿迭代

H(G(x))=G(x)^2-F(x)H(G(x))=G(x)2F(x)
我们继续推式子,这部分就不做解释,指数函数的部分已经讲清楚了。
G_{i+1}(x)\equiv G_i(x)-\frac{H(G(x))}{H'(G(x))}\space({\text{mod }x^n})Gi+1(x)Gi(x)H(G(x))H(G(x)) (mod xn)
G_{i+1}(x)\equiv G_i(x)-\frac{G_i(x)^2-F(x)}{2G_i(x)}\space({\text{mod }x^n})Gi+1(x)Gi(x)2Gi(x)Gi(x)2F(x) (mod xn)
G_{i+1}(x)\equiv \frac{G_i(x)^2+F(x)}{2G_i(x)}\space({\text{mod }x^n})Gi+1(x)2Gi(x)Gi(x)2+F(x) (mod xn)

然后倍增求即可。
注意这里的边界条件是 G(x)G(x) 次数为 00 时,常数项为\sqrt{F(0)}F(0)
也就是解一个二次剩余的方程,具体解法就不在这里写了qwq
\text{Part8——多项式三角函数}Part8——多项式三角函数
一眼看过去,感觉非常不可做。。
实际上挺简单的

先来看一下大名鼎鼎的欧拉公式
e^{ix}=\cos x+i\sin xeix=cosx+isinx
把这个式子稍微变形一下
e^{-ix}=\cos x-i\sin xeix=cosxisinx
将这两个式子相加或相减一下,得到2\cos x=e^{ix}+e^{-ix}2cosx=eix+eix
2i\sin x=e^{ix}-e^{-ix}2isinx=eixeix
然后我们移个项,并用F(x)F(x)代换xx,就得出了结果
\cos F(x)=\frac{e^{iF(x)}+e^{-iF(x)}}{2}cosF(x)=2eiF(x)+eiF(x)
\sin F(x)=\frac{e^{iF(x)}-e^{-iF(x)}}{2i}sinF(x)=2ieiF(x)eiF(x)
可是这里还有个虚数单位ii呢。。这怎么取模啊QAQ
别慌,我们冷静分析一下

根据定义,虚数单位ii满足性质:i^2=-1i2=1
我们想在模意义下计算它,于是列出了一个式子\large i^2\equiv-1\space (\text{mod }p)i21 (mod p)这好像就是解一个二次剩余嘛!
恰好,998244352998244352在模998244353998244353意义下有二次剩余,这个方程的解为8658371886583718
也就是说,上面所有的ii,都用8658371886583718带进去计算就好啦

\text{Part9——多项式反三角函数}Part9——多项式反三角函数
给定一个多项式F(x)F(x),求一个G(x)G(x),满足
\large G(x)\equiv\sin^{-1}F(x)\space (\text{mod }x^n)G(x)sin1F(x) (mod xn)

考虑将两遍求导,得到
G'(x)\equiv \frac{F'(x)}{\sqrt{1-F(x)^2}}\space ({\text{mod }x^n})G(x)1F(x)2F(x) (mod xn)
然后再积分回来
G(x)\equiv \int \frac{F'(x)}{\sqrt{1-F(x)^2}}dx\space ({\text{mod }x^n})G(x)1F(x)2F(x)dx (mod xn)
\tan^{-1}tan1的方式,也差不多
H(x)\equiv\tan^{-1}F(x)\space (\text{mod }x^n)H(x)tan1F(x) (mod xn)
H'(x)\equiv\frac{F'(x)}{1+F(x)^2}\space (\text{mod }x^n)H(x)1+F(x)2F(x) (mod xn)
H(x)\equiv\int\frac{F'(x)}{1+F(x)^2}dx\space (\text{mod }x^n)H(x)1+F(x)2F(x)dx (mod xn)
然后直接计算即可。

\text{Part10——快速差分/前缀和}Part10——快速差分/前缀和
给定一个序列aa,求其k(k\ge 1)k(k1)阶前缀和。

手推一下就会发现,这个和组合数有着微妙的关系
每一项都是原序列中的几项,乘上一个组合数的系数之和。

于是我们就猜想这个其实是卷积:
我们构造一个序列 bb:
b_i=\binom{k+i-1}{k-1}bi=(k1k+i1)
那么 aa 和 bb 的卷积就是答案啦~

要求差分的话也很简单,把 bb 求个逆,再和 aa 卷积即可。

此时 bb 数组就是b_i=\binom{k}{i}(-1)^{k-i}bi=(ik)(1)ki

时间复杂度仍然是\Theta(n\log n)Θ(nlogn)的
\text{Part11——优化线性齐次递推}Part11——优化线性齐次递推
给定一个kk阶线性齐次递推式:
\large a_n=\sum\limits_{i=1}^kf_i\times a_{n-i}an=i=1kfi×ani
给定n,kn,k,以及aa的00 ~ k-1k1项,求a_nan

先讲一下做法,原理就先鸽着(
可以构造一个多项式G(x)G(x):
[x^{k-i}]G(x)=-f_i\space (i\in [1,k])[xki]G(x)=fi (i[1,k])
[x^k]G(x)=1[xk]G(x)=1
然后求一个F(x)F(x):
F(x) = x^n \text{ mod } G(x)F(x)=xn mod G(x)
那么:
a_n=\sum\limits_{i=0}^{k-1}a_i[x^i]F(x)an=i=0k1ai[xi]F(x)

由于这个算法的第二步需要倍增多项式快速幂,没办法用一个 \loglog 的算法,所以时间复杂度为 \Theta(k\log k \log n)Θ(klogklogn)
但是这个复杂度相比矩阵快速幂的 \Theta(k^3\log n)Θ(k3logn) 来说,已经是非常优秀的了。\text{Part12——多项式多点求值}Part12——多项式多点求值咕咕咕了很久,现在来补一下吧(

首先来证明这样一个引理:
f(x)\equiv f(a)\pmod{(x-a)}f(x)f(a)(mod(xa))
设 f(x) = g(x)(x-a)+bf(x)=g(x)(xa)+b,即 b \equiv f(x)\pmod{(x-a)}bf(x)(mod(xa))。
再把 aa 带入上式,得到 f(a)=bf(a)=b,于是得证。

有了这个式子,要求 f(a)f(a),算 f(x) \bmod (x-a)f(x)mod(xa) 就可以了。
虽然看起来没什么用,但别急。

根据上面的引理,还可以得到如下推论:
g(x) \equiv f(x) \bmod \left( \prod\limits_{i=1}^m(x-a_i)\right)g(x)f(x)mod(i=1m(xai))\Rightarrow g(a_i)=f(a_i) \ (i\in [1,m])g(ai)=f(ai) (i[1,m])这样一来就可以分治计算,时间复杂度
( 这里的 nn 为 \max(n,m)max(n,m) )T(n) = 2T(n/2) + \text O(n \log n) = \text O(n \log ^2 n)T(n)=2T(n/2)+O(nlogn)=O(nlog2n)注意那一堆 (x-a_i)(xai) 乘积的多项式需要分治乘预处理。
由于这个确实不好写,所以留个代码链接:Code