请各位看官移至 此处
用多项式每一项的系数表示多项式
用 n n 个横坐标各不相同的点 (x,y) ( x , y ) 来表示一个次数界为 n n 的多项式
系数表达 => => 点值表达
O(n2) O ( n 2 ) 霍纳法则
点值表达 => => 系数表达
O(n2) O ( n 2 ) 拉格朗日公式
A={a0,a1,⋯,an−1} A = { a 0 , a 1 , ⋯ , a n − 1 }
B={a′0,a′1,⋯,a′n−1} B = { a 0 ′ , a 1 ′ , ⋯ , a n − 1 ′ }
C=A+B={a0+a′0,a1+a′1,⋯an−1+a′n−1} C = A + B = { a 0 + a 0 ′ , a 1 + a 1 ′ , ⋯ a n − 1 + a n − 1 ′ }
C=A∗B,cx=∑xi=0ai∗a′x−i C = A ∗ B , c x = ∑ i = 0 x a i ∗ a x − i ′
A={(x0,y0),(x1,y1),⋯,(xn−1,yn−1)} A = { ( x 0 , y 0 ) , ( x 1 , y 1 ) , ⋯ , ( x n − 1 , y n − 1 ) }
B={(x0,y′0),(x1,y′1),⋯,(xn−1,y′n−1)} B = { ( x 0 , y 0 ′ ) , ( x 1 , y 1 ′ ) , ⋯ , ( x n − 1 , y n − 1 ′ ) }
C=A+B={(x0,y0+y′0),(x1,y1+y′1),⋯,(xn−1,yn−1+y′n−1)} C = A + B = { ( x 0 , y 0 + y 0 ′ ) , ( x 1 , y 1 + y 1 ′ ) , ⋯ , ( x n − 1 , y n − 1 + y n − 1 ′ ) }
C=A∗B={(x0,y0∗y′0),(x1,y1∗y′1),⋯,(xn−1,yn−1∗y′n−1)} C = A ∗ B = { ( x 0 , y 0 ∗ y 0 ′ ) , ( x 1 , y 1 ∗ y 1 ′ ) , ⋯ , ( x n − 1 , y n − 1 ∗ y n − 1 ′ ) }
显然的,用系数表达式算多项式乘法简直是龟速 O(n2) O ( n 2 )
而使用点值表达式算多项式乘法则快很多 (O(n)) ( O ( n ) )
可以看出主要若使用点值表达式,主要复杂度在求值和插值(均为 O(n2) O ( n 2 ) )
显然如果求值插值不能优化的话其复杂度甚至是不如系数乘法的(常数大)
所以我们可以利用一些特殊的方式来加速
n n 次单位复数根是满足 wn=1 w n = 1 的复数 w w , n n 次单位复数根恰有 n n 个
对于 k=0,1,⋯,n−1 k = 0 , 1 , ⋯ , n − 1 ,这些根是 e2πikn e 2 π i k n .( eπi=−1,e2πi=1 e π i = − 1 , e 2 π i = 1 )
用复数的指数形式的定义 eui=cos(u)+i∗sin(u) e u i = c o s ( u ) + i ∗ s i n ( u )
可以建立一个以实数为 x x 轴,以虚数为 y y 轴的坐标系
然后这些单位复数根在坐标轴上正好是半径为1的圆上的点
我们将 e2πi1n e 2 π i 1 n 称为主 n n 次单位根,利用它,其他的单位复数根都是其幂次
然后这些单位复数根的乘法就相当于角度的转换
单位复数根之间满足乘法群的性质 (modn) ( mod n )
消去引理: wdkdn=wkn w d n d k = w n k
* 证明: wdkdn=(e2πi1dn)dk=(e2πi1n)k=wkn w d n d k = ( e 2 π i 1 d n ) d k = ( e 2 π i 1 n ) k = w n k
推论: wn2n=−1 w 2 n n = − 1
折半引理: 若 n n 为偶数,则 n n 次单位复数根的平方的集合就是 n2 n 2 次单位复数根的集合,特别的,每个 n2 n 2 次单位复数根出现两次
* 证明: (wkn)2=wkn2,k∈[0,n−1] ( w n k ) 2 = w n 2 k , k ∈ [ 0 , n − 1 ] [消去引理]
对于 n n 次多项式,我们希望取得其在 w0n,w1n,⋯,wn−1n w n 0 , w n 1 , ⋯ , w n n − 1 处的值
相当于求这样一个矩阵:
得到点值表达式以后我们就只需要 O(n) O ( n ) 的点值乘法,这里不再赘述
那么接下来的任务就是插值了,也就是逆 DFT D F T
易得这个矩阵:
但是具体代码中还是有丶东西
看注释吧!
代码如下:
#include
using namespace std;
const int N =100010;
const double pi=acos(-1);
struct Complex {
double x;
double y;
Complex() {};
Complex(double _x,double _y) { x=_x;y=_y; }
Complex operator + (const Complex o) { return Complex(x+o.x,y+o.y); }
Complex operator - (const Complex o) { return Complex(x-o.x,y-o.y); }
Complex operator * (const Complex o) { return Complex(x*o.x-y*o.y,x*o.y+y*o.x); }
}a[N<<2],b[N<<2];
int r[N<<2];
int n,m,nn;
void fft(Complex *now,int f) {
for(int i=0;i//按块将其分到一起
if(ifor(int i=1;i1) {//枚举合并的块的大小
Complex wn(cos(pi/i),f*sin(pi/i));//单位元,若进行逆DFT变换,则反向旋转
for(int j=0;j1)) {//枚举头
Complex w(1,0);
for(int k=0;k//合并,蝴蝶变换
Complex x=now[j+k],y=w*now[j+k+i];
now[j+k]=x+y;
now[j+k+i]=x-y;
}
}
}
if(f==-1)
for(int i=0;iint main() {
scanf("%d%d",&n,&m);
for(int i=0;i<=n;++i) scanf("%lf",&a[i].x);
for(int i=0;i<=m;++i) scanf("%lf",&b[i].x);
m+=n;
for(n=1;n<=m;n<<=1) nn++;
for(int i=0;i>1]>>1)|((i&1)<<(nn-1));}
fft(a,1);fft(b,1);
for(int i=0;i<=n;++i) { a[i]=a[i]*b[i]; }
fft(a,-1);//逆DFT
for(int i=0;i<=m;++i)
printf("%d ",(int)(a[i].x+0.5))//防止精度出锅;
return 0;
}