P3803 【模板】多项式乘法(FFT)。

多项式基础知识

点值表示法

一个 nn 次多项式可以被 (xi,yi)(x_i,y_i) 这种的 n+1n+1 个点表示。

比较显然的。


FFT 就是利用这种性质,来求解答案。我们需要一个快速将系数表示法转换成点表示法的东西。

复数

这里面还需要一些复数的性质,先看看复数相乘:

(a+bi)∗(c+di)=ac+adi+bci+bdi2=ac+adi+bci−bd=(ac−bd)+(bc+ad)i\begin{align*} (a+bi)*(c+di) &= ac+adi+bci+bdi^2\\ &= ac+adi+bci-bd\\ &= (ac-bd)+(bc+ad)i \end{align*}

为了方便说明,nn 先设为 22 的整次幂。在复平面上,以单位圆的 nn 等分点为终点,做 nn 个向量,ωn2,ωn3,…,ωnn\omega_n^2,\omega_n^3,\ldots,\omega_n^n。

性质:

ωnk=ω2n2k\omega^{k}_n=\omega^{2k}_{2n} ωnn=1ωnn2=−1\omega^n_n=1\\ \omega^{\frac n 2}_n=-1 z2=(a+bi)2=a2+2abi+(bi)2=a2−b2+2abiz2=(cos⁡2θ−sin⁡2θ,  2cos⁡θsin⁡θ)=(cos⁡2θ,  sin⁡2θ)z^2 = (a+bi)^2 = a^2 + 2abi + (bi)^2 = a^2 - b^2 + 2abi\\ z^2 = (\cos^2\theta - \sin^2\theta,\; 2\cos\theta\sin\theta) = (\cos 2\theta,\; \sin 2\theta)

快速傅里叶变换

正变换

A(x)=a0+a1∗x+a2∗x2+a3∗x3+a4∗x4+a5∗x5+⋯+an−2∗xn−2+an−1∗xn−1A(x)=a_0+a_1*x+a_2*{x^2}+a_3*{x^3}+a_4*{x^4}+a_5*{x^5}+ \dots+a_{n-2}*x^{n-2}+a_{n-1}*x^{n-1}

按照奇偶性拆分:

A1(x)=a0+a2∗x+a4∗x2+⋯+an−2∗xn2−1A_1(x)=a_0+a_2*{x}+a_4*{x^2}+\dots+a_{n-2}*x^{\frac{n}{2}-1}

A2(x)=a1∗x+a3∗x+a5∗x2+⋯+an−1∗xn2−1A_2(x)=a_1*x+a_3*{x}+a_5*{x^2}+ \dots+a_{n-1}*x^{\frac{n}{2}-1}

所以:

A(x)=A1(x2)+xA2(x2)A(x)=A_1(x^2)+xA_2(x^2)

A(ωnk)=A1(ωn2k)+ωnkA2(ωn2k)A(\omega_n^k)=A_1(\omega_n^{2k})+\omega_n^kA_2(\omega_n^{2k})


A(ωnk+n2)=A1(ωn2k+n)+ωnk+n2(ωn2k+n)=A1(ωn2k×ωnn)−ωnkA2(ωn2k×ωnn)=A1(ωn2k)−ωnkA2(ωn2k)\begin{align*} A(\omega_n^{k+\frac{n}{2}})\\ &=A_1(\omega_n^{2k+n})+\omega_n^{k+\frac{n}{2}}(\omega_n^{2k+n})\\ &=A_1(\omega_n^{2k}\times\omega_n^n)-\omega_n^kA_2(\omega_n^{2k}\times\omega_n^n)\\ &=A_1(\omega_n^{2k})-\omega_n^kA_2(\omega_n^{2k}) \end{align*}

--

对比这两个式子:

A(ωnk)=A1(ωn2k)+ωnkA2(ωn2k)A(\omega_n^k)=A_1(\omega_n^{2k})+\omega_n^kA_2(\omega_n^{2k})

A(ωnk+n2)=A1(ωn2k)−ωnkA2(ωn2k)A(\omega_n^{k+\frac{n}{2}})=A_1(\omega_n^{2k})-\omega_n^kA_2(\omega_n^{2k})

所以我们枚举第一个式子的时候可以 O(1)O(1) 计算第二个式子的值。

逆变换

  • 系数 → 点值(DFT,正变换)

  • 点值 → 系数(IDFT,逆变换)

我们还需要一个逆变换。

nA(x)=c0+c1x+c2x2+⋯+cn−1xn−1ak=cknnA(x) = c_0 + c_1 x + c_2 x^2 + \dots + c_{n-1} x^{n-1}\\ a_k=\frac{c_k}{n} ck=∑i=0n−1yi (ωn−k)ic_k = \sum_{i=0}^{n-1} y_i \, (\omega_n^{-k})^i

B(x)=y0+y1x+y2x2+⋯+yn−1xn−1B(x)=y_0+y_1x+y_2x^2+\dots+y_{n-1}x^{n-1}

ck=B(ωn−k)c_k=B(\omega_n^{-k})

怎么证明?

将 yi=∑j=0n−1ajωnijy_i = \sum_{j=0}^{n-1} a_j \omega_n^{ij} 代入 ckc_k:

ck=∑i=0n−1(∑j=0n−1ajωnij)ωn−ik=∑j=0n−1aj∑i=0n−1ωni(j−k)c_k = \sum_{i=0}^{n-1} \left( \sum_{j=0}^{n-1} a_j \omega_n^{ij} \right) \omega_n^{-ik} = \sum_{j=0}^{n-1} a_j \sum_{i=0}^{n-1} \omega_n^{i(j-k)}

内层和式 S=∑i=0n−1(ωnj−k)iS = \sum_{i=0}^{n-1} (\omega_n^{j-k})^i 是等比数列求和:

  • 若 j=kj = k,则 ωn0=1\omega_n^{0}=1,S=nS = n
  • 若 j≠kj \neq k,则 ωnj−k≠1\omega_n^{j-k} \neq 1,且 (ωnj−k)n=1(\omega_n^{j-k})^n = 1,由等比数列求和公式得:
S=1−(ωnj−k)n1−ωnj−k=1−11−ωnj−k=0S = \frac{1 - (\omega_n^{j-k})^n}{1 - \omega_n^{j-k}} = \frac{1-1}{1-\omega_n^{j-k}} = 0

因此 ck=ak⋅nc_k = a_k \cdot n,即 ak=ck/na_k = c_k / n。证毕。

所以逆变换公式就是:

ak=1n∑i=0n−1yi ωn−ika_k = \frac{1}{n} \sum_{i=0}^{n-1} y_i \, \omega_n^{-ik}

公式整合

  • 正变换(DFT):
    yk=∑j=0n−1aj ωnjky_k = \sum_{j=0}^{n-1} a_j \, \omega_n^{jk}

  • 逆变换(IDFT):
    aj=1n∑k=0n−1yk ωn−jka_j = \frac{1}{n} \sum_{k=0}^{n-1} y_k \, \omega_n^{-jk}

也就是逆变换只需要把正变换的 ω\omega 取一个相反数就行了。

A(ωnk+n2)=A1(ωn2k)−ωnkA2(ωn2k)A(\omega_n^{k+\frac{n}{2}})=A_1(\omega_n^{2k})-\omega_n^kA_2(\omega_n^{2k})

小优化:

我们可以把 b(x)b(x) 放到 a(x)a(x) 的虚部上去,求出a(x)2a(x)^2,然后把 a(x)a(x) 的虚部取出来除 22 就是答案了。

(a+bi)2=(a2−b2)+(2abi)\large (a+bi)^2=(a^2-b^2)+(2abi)

这样就只需要 33 次 FFT 了。

P4245 【模板】任意模数多项式乘法

P4245 【模板】任意模数多项式乘法。

我们发现如果直接用 FFT 中 double 精度不够,102310^{23} 完全不行。

A(x)=A0(x)⋅M+A1(x),B(x)=B0(x)⋅M+B1(x)A(x) = A_0(x) \cdot M + A_1(x), \quad B(x) = B_0(x) \cdot M + B_1(x) A⋅B=(A0B0)M2+(A0B1+A1B0)M+A1B1A\cdot B = (A_0B_0) M^2 + (A_0B_1 + A_1B_0) M + A_1B_1

我们只需要用 44 次 FFT 递归下去(显然递推也行),求出这四个值加起来。

Pk=(A0)k(B0)k+i⋅(A1)k(B0)kQk=(A0)k(B1)k+i⋅(A1)k(B1)kP_k = (A_0)_k (B_0)_k + i \cdot (A_1)_k (B_0)_k\\ Q_k = (A_0)_k (B_1)_k + i \cdot (A_1)_k (B_1)_k

这样的话只有不到 101410^{14} 次。