发现自己的多项式水平菜到不行,所以写篇文章记录一下。

基础操作

FFT & NTT

基础知识。

考虑如何快速计算多项式乘法,发现常规的做法是没有前途的,考虑换种方法计算。

有一种计算方法是这样的(设要计算 A×BA\times B ):

  • AA 转化为点值表示。
  • BB 转化为点值表示。
  • O(n)O(n) 地计算点值表示的乘积 CC
  • CC 转化为数值表示。

复杂度瓶颈为点值表示与数值表示的转化,朴素做是 O(n2)O(n^2)

为了加速这个过程,FFT 引入了单位复根 ω\omega,NTT 引入了原根 gg

单位复根 ω\omegaxn=1x^n=1 的解集,其满足如下性质:

  • ωnn=1\omega^n_n=1
  • ωnk=ω2n2k\omega^k_n=\omega^{2k}_{2n}
  • ω2nk+n=ω2nk\omega^{k+n}_{2n}=-\omega^k_{2n}

考虑分治,对一个多项式 f(x)f(x) 奇偶性分组处理,比如一个多项式:

f(x)=a0+a1x+a2x2+a3x3+a4x4+a5x5+a6x6+a7x7f(x)=a_0+a_1x+a_2x^2+a_3x^3+a_4x^4+a_5x^5+a_6x^6+a_7x^7

考虑奇偶性分组后的状态:

f(x)=a0+a2x2+a4x4+a6x6+x(a1+a3x2+a5x4+a7x6)f(x)=a_0+a_2x^2+a_4x^4+a_6x^6+x(a_1+a_3x^2+a_5x^4+a_7x^6)

建立新函数:

G(x)=a0+a2x+a4x2+a6x3H(x)=a1+a3+a5x2+a7x3G(x)=a_0+a_2x+a_4x^2+a_6x^3\\H(x)=a_1+a_3+a_5x^2+a_7x^3

那么待求解的 f(x)f(x) 可改为:

f(x)=G(x2)+xH(x2)f(x)=G(x^2)+xH(x^2)

带入 ω\omega 可得:

DFT(f(ωnk))=DFT(G((ωnk)2))+ωnk×DFT(H((ωnk)2))=DFT(G(ωn2k))+ωnk×DFT(H(ωn2k))=DFT(G(ωn/2k))+ωnk×DFT(H(ωn/2k))DFT(f(ωnk+n/2))=DFT(G(ωn2k+n))+ωnk+n/2×DFT(H(ωn2k+n))=DFT(G(ωn2k))ωnk×DFT(H(ωn2k))=DFT(G(ωn/2k))ωnk×DFT(H(ωn/2k))\begin{aligned} \operatorname{DFT}(f(\omega_n^k)) &=\operatorname{DFT}(G((\omega_n^k)^2)) + \omega_n^k \times \operatorname{DFT}(H((\omega_n^k)^2))\\ &=\operatorname{DFT}(G(\omega_n^{2k})) + \omega_n^k \times \operatorname{DFT}(H(\omega_n^{2k}))\\ &=\operatorname{DFT}(G(\omega_{n/2}^k)) + \omega_n^k \times \operatorname{DFT}(H(\omega_{n/2}^k)) \end{aligned} \\ \begin{aligned} \operatorname{DFT}(f(\omega_n^{k+n/2})) &=\operatorname{DFT}(G(\omega_n^{2k+n})) + \omega_n^{k+n/2} \times \operatorname{DFT}(H(\omega_n^{2k+n}))\\ &=\operatorname{DFT}(G(\omega_n^{2k})) - \omega_n^k \times \operatorname{DFT}(H(\omega_n^{2k}))\\ &=\operatorname{DFT}(G(\omega_{n/2}^k)) - \omega_n^k \times \operatorname{DFT}(H(\omega_{n/2}^k)) \end{aligned}

然后就可以分治做了。

诶等等,递归常数特别大呢!

为了不递归,我们考虑一开始就预处理出来每一项最后到了什么地方,这个做法是位逆序置换(又名蝴蝶置换)。

考虑找规律,发现将下标二进制表示后翻转即可,设 RiR_iii 变到的地方,那么可以考虑这样求:

  • 右移一位。
  • 翻转。
  • 再右移一位。
  • 处理最高位。

那么 RiR_i 的递推式可以轻易的写成:

Ri=Ri22+(imod2)×len2R_i=\lfloor\frac{R_{\lfloor\frac{i}{2}\rfloor}}{2}\rfloor+(i\bmod2)\times\frac{len}{2}

以上就是如何将数值表示转化为点值表示了,所以怎样转回来呢?

直接反转后除以 lenlen,或是取单位根的倒数跑即可,具体推导过程见 OI-Wiki

模板:

void FFT(int n,cp* x,int op) {
    for(int i=0;i<n;i++)
        if(i<rk[i]) swap(x[i],x[rk[i]]);
    for(int mid=1;mid<n;mid*=2) {
        cp W(cos(Pi/mid),op*sin(Pi/mid));
        for(int R=mid*2,j=0;j<n;j+=R) {
            cp w(1,0);
            for(int k=0;k<mid;k++,w=w*W) {
                cp z1=x[j+k],z2=w*x[j+mid+k];
                x[j+k]=z1+z2,x[j+mid+k]=z1-z2;                
            }						            
        }				        
    }	    
}

NTT 呢?

首先解释 gggg 是原根,在 modp\bmod p 的情况下 i=j,gigj(modp)\forall i\not =j,g^i\not\equiv g^j\pmod p

我们用 gφ(p)ng^{\frac{\varphi(p)}{n}} 代替 ω\omega 进行计算,由 φ(p)=p1\varphi(p)=p-1n=2qn=2^q,所以强制要求 p=k×2q+1p=k\times 2^q+1,其中 2qn2^q\ge n

较 FFT 精度高,但却对模数有着致命的要求。

时间复杂度 O(nlogn)O(n\log n)

分治 FFT & NTT

问题:求 fnf_n,其中:

fi=j=0i=1fjgijf_i=\sum^{i=1}_{j=0} f_jg_{i-j}

考虑 CDQ 分治,对于一个区间 [l,r][l,r],将其划分成 [l,mid],[mid+1,r][l,mid],[mid+1,r] 两段。

先处理 [l,mid][l,mid],再计算 [l,mid][l,mid][mid+1,r][mid+1,r] 的贡献,再处理 [mid+1,r][mid+1,r]

中间的计算使用 NTT/FFT 解决,时间复杂度 O(nlog2n)O(n\log^2n)

拉格朗日插值

问题,给定 nn 个点 (xi,yi)(x_i,y_i),确定一个多项式 y=f(x)y=f(x),求出 f(k)f(k) 的值。

显然的事实:

f(x)f(y)(modxy)f(x)\equiv f(y)\pmod{x-y}

证明:f(x)f(y)=(a0a0)+a1(xy)+a2(x2y2)+a3(x3y3)+f(x)-f(y)=(a_0-a_0)+\color{red}a_1(x-y)\color{black}+a_2(x^2-y^2)+a_3(x^3-y^3)+\cdots

那么我们就可以列一个线性同余方程组:

{f(x)y1(modxy1)f(x)y2(modxy2)f(x)yn(modxyn)\begin{cases}f(x)\equiv y_1\pmod {x-y_1}\\f(x)\equiv y_2\pmod{x-y_2}\\\ldots\\f(x)\equiv y_n\pmod{x-y_n}\end{cases}

直接 CRT,所有模数的积为:

i=1n(xyi)\prod_{i=1}^n (x-y_i)

那么 mi=j=i(xyi),mi1=j=i1xixjm_i=\prod _{j\not=i} (x-y_i),m_i^{-1}=\prod _{j\not=i} \frac {1}{x_i-x_j}

所以:

f(x)=i=1nyij=ixxjxixjf(x)=\sum^n_{i=1} y_i \prod_{j\not=i} \frac {x-x_j}{x_i-x_j}

朴素实现复杂度是 O(n2)O(n^2) 的。

在所取点值连续的时候,即 xi=ix_i=i 的时候,可以直接用前缀积优化到 O(n)O(n)

在需要动态加点的时候,可以使用重心拉格朗日插值法:

回顾之前的式子:

f(x)=i=1nyij=ixxjxixjf(x)=\sum^n_{i=1} y_i \prod_{j\not=i} \frac {x-x_j}{x_i-x_j}

g=i=1nkxig=\prod^n_{i=1} k-x_i,那么:

f(x)=gi=0nyi(kxi)(xixj)f(x)=g\sum^n_{i=0}\prod\frac {y_i}{(k-x_i)(x_i-x_j)}

ti=yij=ixixjt_i=\frac {y_i}{\prod_{j\not=i} x_i-x_j},那么:

f(x)=gi=0ntikxif(x)=g\sum^n_{i=0}\frac {t_i}{k-x_i}

加入一个点只需计算 tit_i 即可。

求自然数幂和可以直接拉格朗日插值,时间复杂度 O(n)O(n)

多项式求导与积分

由于求导和积分满足可加性,所以拆成单项式分别求导或积分。

如果你学过微积分相关理论,会知道:

A(x)=i=1niaixi1A(x)dx=(i=0naii+1xi+1)+cA'(x)=\sum^n_{i=1}ia_ix^{i-1}\\\int A(x)dx=(\sum^n_{i=0}\frac {a_i}{i+1}x^{i+1})+c

直接做即可,为数不多的 O(n)O(n) 时间复杂度。

多项式求逆

给定 G(x)G(x),求一个 F(x)F(x) 使得 F(x)G(x)0(modxn)F(x)G(x)\equiv0\pmod {x^n}

首先假设我们已经求出了一个 f(x)f(x) 使得 f(x)G(x)0(modxn/2)f(x)G(x)\equiv 0\pmod{x^{n/2}},考虑如何求出 F(x)F(x)

因为 F(x)G(x)0(modxn)F(x)G(x)\equiv 0\pmod{x^n},所以必然 F(x)G(x)0(modxn/2)F(x)G(x)\equiv 0 \pmod{x^{n/2}},两式相减,得 F(x)f(x)0(modxn/2)F(x)-f(x)\equiv0\pmod{x^{n/2}},此处 G(x)G(x) 被约掉了。

两边同时平方,得:

F2(x)2F(x)f(x)+f2(x)0(modxn)F^2(x)-2F(x)f(x)+f^2(x)\equiv0\color{red}{\pmod{x^{n}}}

注意标红的部分,想一想,为什么?

考虑卷积的定义,在 [0,n/2][0,n/2] 的部分每一项都是 00,而右边 [n/2,n][n/2,n] 的部分由 ai=j=1iajaija_i=\sum^i_{j=1} a_ja_{i-j} 得来,容易发现,jjiji-j 中必有一个小于零。

两边同时乘以 G(x)G(x) 并移项,得:

F(x)2f(x)G(x)f2(x)(modxn)F(x)\equiv 2f(x)-G(x)f^2(x)\pmod{x^n}

直接使用 FFT 计算即可,时间复杂度 O(nlogn)O(n\log n)

多项式开根

给定 g(x)g(x),求 f(x)f(x),使得:f2(x)g(x)(modxn)f^2(x)\equiv g(x)\pmod{x^n}

类似求逆,假设已经求出了 f02(x)g(x)(modxn/2)f_0^2(x)\equiv g(x)\pmod{x^{n/2}}

移项得:

f02(x)g(x)0(modxn/2)f_0^2(x)-g(x)\equiv 0\pmod{x^{n/2}}

两边平方得:

(f02(x)g(x))20(modxn)(f_0^2(x)-g(x))^2\equiv 0\pmod {x^n}

使用简单的数学公式 (xy)2=(x+y)24xy(x-y)^2=(x+y)^2-4xy 得:

(f02(x)+g(x))24f02(x)g(x)(modxn)(f_0^2(x)+g(x))^2\equiv 4f_0^2(x)g(x)\pmod {x^n}

4f02(x)4f_0^2(x) 移到左边,得:

(f02(x)+g(x)2f0(x))2g(x)(modxn)(\frac{f_0^2(x)+g(x)}{2f_0(x)})^2\equiv g(x) \pmod{x^n}

左右两边同时开根,得:

f02(x)+g(x)2f0(x)f(x)(modxn)\frac{f_0^2(x)+g(x)}{2f_0(x)} \equiv f(x)\pmod{x^n}

21f0(x)+21f01(x)g(x)f(x)(modxn)2^{-1}f_0(x)+2^{-1}f_0^{-1}(x)g(x)\equiv f(x)\pmod{x^n}

直接计算即可,时间复杂度 O(nlogn)O(n\log n)

多项式带余除法

问题:给定 nn 次多项式 F(x)F(x)mm 次多项式 G(x)G(x),求 Q(x)Q(x)R(x)R(x),满足:

  • degQ(x)=nm\deg Q(x)=n-mdegR(x)<m\deg R(x)<m
  • F(x)=Q(x)×G(x)+R(x)F(x)=Q(x)\times G(x)+R(x)

对于一个多项式 f(x)f(x),定义 fR(x)=xnf(1x)f_R(x)=x^nf(\frac 1 x),等价于将多项式 ff 的系数翻转一次。

那么:

F(x)=Q(x)×G(x)+R(x)F(1x)=Q(1x)×G(1x)+R(1x)xnF(1x)=xnmQ(1x)×xmG(1x)+xnm+1xm1R(1x)FR(x)=QR(x)×GR(x)+xnm+1RR(x)FR(x)QR(x)×GR(x)(modxnm+1)QR(x)FR(x)×GR1(x)(modxnm+1)F(x)=Q(x)\times G(x)+R(x)\\F(\frac 1 x)=Q(\frac 1 x)\times G(\frac 1 x)+R(\frac 1 x)\\x^nF(\frac 1 x)=x^{n-m} Q(\frac 1 x)\times x^mG(\frac 1 x)+x^{n-m+1}x^{m-1}R(\frac 1 x)\\F_R(x)=Q_R(x)\times G_R(x)+x^{n-m+1}R_R(x)\\F_R(x)\equiv Q_R(x)\times G_R(x)\pmod {x^{n-m+1}}\\Q_R(x)\equiv F_R(x)\times G_R^{-1}(x) \pmod{x^{n-m+1}}

直接求出 Q(x)Q(x),那么回代可得 R(x)R(x),时间复杂度 O(nlogn)O(n\log n)

多项式对数函数

B(x)lnA(x)(modxn)B(x)\equiv \ln A(x)\pmod{x^n}

F(x)=lnxF(x)=\ln x,那么:

B(x)=F(A(x))B(x)=F(A(x))A(x)B(x)=A(x)A(x)B(x)=A(x)dxA(x)B(x)=F(A(x))\\B'(x)=F'(A(x))A'(x)\\B'(x)=\frac {A'(x)}{A(x)}\\B(x)=\int \frac {A'(x)dx}{A(x)}

直接模拟即可,时间复杂度 O(nlogn)O(n\log n)

多项式指数函数

F(x)eA(x)(modxn)F(x)\equiv e^{A(x) \pmod{x^n}}

考虑多项式牛顿迭代。

多项式牛顿迭代解决的问题是,给定函数 G(x)G(x),求 G(F(x))0(modxn)G(F(x))\equiv 0\pmod {x^n}

既然是迭代,那么当 n=1n=1 的时候是要单独求出的,这里不讲。

类似求逆,假设已经求出 G(F0(x))0(modxnx)G(F_0(x))\equiv 0 \pmod{x^{\lceil \frac n x\rceil}},考虑如何扩展。

G(F(x))G(F(x))F0(x)F_0(x) 处泰勒展开:

G(F(x))=G(F0(x))+G(F0(x))1!(F(x)F0(x))+G(F0(x))2!(F(x)F0(x))+G(F(x))=G(F_0(x))+\frac {G'(F_0(x))}{1!}(F(x)-F_0(x))+\frac {G''(F_0(x))}{2!}(F(x)-F_0(x))+\ldots

因为 F(x)F(x)F0(x)F_0(x) 的最后几项相同,所以 (F(x)F0(x))2(F(x)-F_0(x))^2 的最低非 00 次数大于 nn,所以可以得到:

G(F(x))G(F0(x))+G(F0(x))(F(x)F0(x))(modxn)G(F0(x))+G(F0(x))(F(x)F0(x))0(modxn)G(F0(x))+G(F0(x))F(x)G(F0(x))F0(x)0(modxn)G(F0(x))F(x)G(F0(x))F0(x)G(F0(x))(modxn)F(x)F0(x)G(F0(x))G(F0(x))(modxn)G(F(x))\equiv G(F_0(x))+G'(F_0(x))(F(x)-F_0(x))\pmod{x^n}\\G(F_0(x))+G'(F_0(x))(F(x)-F_0(x))\equiv 0\pmod{x^n}\\G(F_0(x))+G'(F_0(x))F(x)-G'(F_0(x))F_0(x)\equiv 0\pmod{x^n}\\G'(F_0(x))F(x)\equiv G'(F_0(x))F_0(x)-G(F_0(x))\pmod {x^n}\\F(x)\equiv F_0(x)-\frac {G(F_0(x))}{G'(F_0(x))} \pmod {x^n}

那么,将 F(x)=eA(x)F(x)=e^{A(x)} 变形得 lnF(x)A(x)=0\ln F(x)-A(x)=0

G(F(x))=lnF(x)A(x)G(F(x))=\ln F(x)-A(x),那么 G(F(x))=1F(x)G'(F(x))=\frac 1 {F(x)},带入牛顿迭代公式得:

F(x)F0(x)lnF0(x)A(x)F0(x)(modxn)F(x)F0(x)(1lnF0(x)+A(x))(modxn)F(x)\equiv F_0(x)-\frac{\ln F_0(x)-A(x)}{F_0(x)}\pmod{x^n}\\F(x)\equiv F_0(x)(1-\ln F_0(x)+A(x))\pmod{x^n}

直接做,时间复杂度 O(nlogn)O(n\log n)

参考代码

不想太长,所以放一份在剪贴板