請各位看官移至 此處
簡單的預備知識
係數表達
用多項式每一項的係數表示多項式
點值表達
用 個橫座標各不相同的點 來表示一個次數界爲 的多項式
求值
係數表達 點值表達
霍納法則
插值
點值表達 係數表達
拉格朗日公式
係數表達式求多項式加,乘法
點值表達式求多項式加,乘法
顯然的,用係數表達式算多項式乘法簡直是龜速
而使用點值表達式算多項式乘法則快很多
可以看出主要若使用點值表達式,主要複雜度在求值和插值(均爲 )
顯然如果求值插值不能優化的話其複雜度甚至是不如係數乘法的(常數大)
所以我們可以利用一些特殊的方式來加速
引入單位複數根
次單位複數根是滿足 的複數 , 次單位複數根恰有 個
對於 ,這些根是 .( )
用複數的指數形式的定義
可以建立一個以實數爲 軸,以虛數爲 軸的座標系
然後這些單位複數根在座標軸上正好是半徑爲1的圓上的點
我們將 稱爲主 次單位根,利用它,其他的單位複數根都是其冪次
然後這些單位複數根的乘法就相當於角度的轉換
單位複數根之間滿足乘法羣的性質
消去引理:
* 證明:
推論:
折半引理: 若 爲偶數,則 次單位複數根的平方的集合就是 次單位複數根的集合,特別的,每個 次單位複數根出現兩次
* 證明: [消去引理]
正式開始
對於 次多項式,我們希望取得其在 處的值
相當於求這樣一個矩陣:
其中 爲取到 時的點值, 爲第 位的係數
現定義:
易得:
因爲當 和 時,兩數平方相等
所以說我們的取值集合就縮小了一半
這樣一直遞歸下去
每層有 個塊,每個塊有 個取值,所以一共只需計算 次,共 層,總時間複雜度爲
非常的
這個過程也叫做
得到點值表達式以後我們就只需要 的點值乘法,這裏不再贅述
那麼接下來的任務就是插值了,也就是逆
易得這個矩陣:
其中 爲取到 時的點值, 爲第 位的係數
所以
單位矩陣
所以 中位於第 行第 列(均從0開始標號)的元素爲
所以
是不是和原來的公式很像?
只不過 的係數變成了負的
多了一個 而已
發現也可以 解決
這樣就做完啦!
但是具體代碼中還是有丶東西
看註釋吧!
代碼如下:
#include <bits/stdc++.h>
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<n;++i)//按塊將其分到一起
if(i<r[i]) swap(now[i],now[r[i]]);
for(int i=1;i<n;i<<=1) {//枚舉合併的塊的大小
Complex wn(cos(pi/i),f*sin(pi/i));//單位元,若進行逆DFT變換,則反向旋轉
for(int j=0;j<n;j+=(i<<1)) {//枚舉頭
Complex w(1,0);
for(int k=0;k<i;++k,w=w*wn) {//合併,蝴蝶變換
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;i<n;++i)
now[i].x/=n;
}
int 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<n;++i) {r[i]=(r[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;
}