部分内容引自command_block的博客,OI Wiki多项式求逆部分,Great_Influence的博客
多项式基础知识
多项式的表示法
系数表示法
$$f(x)=\sum\limits_{i=0}^na_i*x^i$$
点值表示法
引理:$n+1$个不同的点可以唯一确定一个最高为$n$次的多项式
给出$n+1$维二元向量组$(\vec{x},\vec{y})$,满足$f(x_i)=y_i(\forall i,j\ s.t.\ x_i\ne x_j)$,确定一个多项式的形式
求值和插值
多项式求值:将系数表示转化为点值表示
多项式插值:将点值表示转化为系数表示
多项式插值及上述引理的解释:
多项式插值的本质是范德蒙矩阵求逆
$$\begin{bmatrix}1&x_1&x_1^2&\cdots&x_1^n\\1&x_2&x_2^2&\cdots&x_2^n\\\vdots&\vdots&\vdots&\ddots&\vdots\\1&x_{n+1}&x^2_{n+1}&\cdots&x^n_{n+1}\end{bmatrix}\begin{bmatrix}a_0\\a_1\\\vdots\\a_n\end{bmatrix}=\begin{bmatrix}y_1\\y_2\\\vdots\\y_{n+1}\end{bmatrix}$$
可以证明若$x_i$各不相同则范德蒙矩阵一定可逆,则系数向量$\vec{a}$有唯一解
多项式加减法
系数表示法
$$A(x)±B(x)=\sum\limits_{i=0}^n(a_i±b_i)*x^i$$
点值表示法
$$(\vec{x},\vec{y_a})±(\vec{x},\vec{y_b})=(\vec{x},\vec{y_a}±\vec{y_b})$$
多项式乘法
系数表示法
$$A(x)×B(x)=\sum\limits_{i=0}^n\sum\limits_{j=0}^na_i×b_j×x^{i+j}=\sum\limits_{i=0}^{2n}\sum\limits_{j=0}^{min(i,n)}a_j×b_{i-j}×x^i$$
点值表示法(向量$\vec{x},\vec{y_a},\vec{y_b}$应为$2n+1$维以确定$2n$次多项式)
$$(\vec{x},\vec{y_a})*(\vec{x},\vec{y_b})=(\vec{x},\vec{y}),y_k=y_{a_k}y_{b_k}$$
卷积运算
$$c_k=\sum\limits_{i⊕j=k}a_ib_j$$
其中⊕为某种运算
点值表示下的多项式乘法即为加法卷积
拉格朗日插值法
给定$n+1$个互不相同的点,确定$n$次多项式$f(x)$, 第$i$个点的坐标为$(x_i,y_i)$,我们需要找到该多项式在$k$点的取值,根据拉格朗日插值法有
$$f(k)=\sum\limits_{i=0}^ny_i\prod\limits_{i\ne j}\frac{k-x[j]}{x[i]-x[j]}$$
复杂度为$O(n^2)$
快速傅里叶变换$(FFT)$
基本思想:将系数表达转化为点值表达,利用点值表达相乘计算多项式乘法,再将点值表达相乘的结果转化为系数表达
将系数表达转化为点值表达$(DFT)$:
若$n$不是$2$的整数次幂,则补若干系数为$0$的项使$n$成为$2$的整数次幂
对$n$次多项式$f(x)=\sum\limits_{i=0}^na_ix^i$,按$x$次数的奇偶性划分可得$f(x)=\sum\limits_{i=0}^{2i\le n}a_{2i}x^{2i}+\sum\limits_{i=0}^{2i+1\le n}a_{2i+1}x^{2i+1}$
令多项式
$f_1(x)=\sum\limits_{i=0}^{2i\le n}a_{2i}x^{i},f_2(x)=\sum\limits_{i=0}^{2i+1\le n}a_{2i+1}x^{i}$
则$f(x)=f_1(x^2)+xf_2(x^2)$
分别代入$w_{n+1}^k,w_{n+1}^{k+\frac{n+1}{2}}$可得
$$f(w_{n+1}^k)=f_1(w_{\frac{n+1}{2}}^k)+w_{n+1}^kf_2(w_{\frac{n+1}{2}}^k)\qquad(1)$$
$$f(w_{n+1}^{k+\frac{n+1}{2}})=f_1(w_{\frac{n+1}{2}}^k)-w_{n+1}^kf_2(w_{\frac{n+1}{2}}^k)\qquad(2)$$
所以如果我们知道两个多项式$f_1(x)$和$f_2(x)$分别在$w_{\frac{n+1}{2}}^0...w_{\frac{n+1}{2}}^{\frac{n+1}{2}-1}$的点值表示
利用式$(1)$可求$f(x)$在$w_{n+1}^0...w_{n+1}^{\frac{n+1}{2}-1}$的点值表示,利用式$(2)$可求$f(x)$在$w_{n+1}^{\frac{n+1}{2}}...w_{n+1}^{n}$的点值表示,时间复杂度为$O(n)$
而对于$f_1(x),f_2(x)$也满足同样的性质,递归求解即可,时间复杂度$O(nlogn)$
单位根求解:$w_n^k=cos\frac{2\pi k}{n}+i*sin\frac{2\pi k}{n}$
将点值表达转化为系数表达$(IDFT)$
设$g=DFT(f)$即$g(k)=\sum\limits_{i=0}^n(w_{n+1}^k)^if(i)$
则有$f=IDFT(g)$即$f(k)=\frac{1}{n}\sum\limits_{i=0}^n(w_{n+1}^{-k})^ig(i)$
挂一手代码,朴素递归写法(多项式乘法)
#include<iostream> #include<cstdio> #include<cmath> #define PAI acos(-1) using namespace std; const int maxn=4e6+10; struct complex{ double a,b; }f[maxn],g[maxn],mul[maxn]; int n,m,N; complex operator *(const complex &x,const complex &y) { return (complex){x.a*y.a-x.b*y.b,x.a*y.b+x.b*y.a}; } complex operator +(const complex &x,const complex &y) { return (complex){x.a+y.a,x.b+y.b}; } complex operator -(const complex &x,const complex &y) { return (complex){x.a-y.a,x.b-y.b}; } int up(int x) { int ret=1; for(;ret<x;ret*=2); return ret; } complex makew(int len,int k) { return (complex){cos(2*PAI*k/len),sin(2*PAI*k/len)}; } void fft(int ty,int len,complex *a) { if(len==1) return; complex f1[len/2],f2[len/2]; for(int i=0;2*i<len;i++) f1[i]=a[2*i]; for(int i=0;2*i+1<len;i++) f2[i]=a[2*i+1]; fft(ty,len/2,f1),fft(ty,len/2,f2); for(int i=0;i<len/2;i++) { complex w=makew(len,ty*i); a[i]=f1[i]+w*f2[i]; a[i+len/2]=f1[i]-w*f2[i]; } } int main() { scanf("%d%d",&n,&m); N=up(n+m+1); for(int i=0;i<=n;i++) scanf("%lf",&f[i].a); for(int i=0;i<=m;i++) scanf("%lf",&g[i].a); fft(1,N,f),fft(1,N,g); for(int i=0;i<N;i++) mul[i]=f[i]*g[i]; fft(-1,N,mul); for(int i=0;i<n+m+1;i++) printf("%d ",(int)(mul[i].a/N+0.5)); printf("\n"); return 0; }