[uoj#34] [洛谷P3803] 多項式乘法(FFT)
阿新 • • 發佈:2018-03-06
spl logs con 常見 scanf printf namespace while 神奇
新技能——FFT。
可在 \(O(nlogn)\) 時間內完成多項式在系數表達與點值表達之間的轉換。
其中最關鍵的一點便為單位復數根,有神奇的折半性質。
多項式乘法(即為卷積)的常見形式:
\[
C_n=\sum\limits_{i=0}^n A_iB_{n-i}
\]
基本思路為先將系數表達 -> 點值表達 \(O(nlogn)\)
隨後點值 \(O(n)\) 進行乘法運算
最後將點值表達 -> 系數表達 \(O(nlogn)\)
代碼
#include<cstdio> #include<iostream> #include<algorithm> #include<cmath> using namespace std; const int N = 2100005; const double pi = 3.1415926535897932384626433832795; struct c{ double r,i; c() { r=i=0.0; } c(double x,double y) { r=x; i=y; } c operator + (const c &b) { return c(r+b.r,i+b.i); } c operator += (const c &b) { return *this=*this+b; } c operator - (const c &b) { return c(r-b.r,i-b.i); } c operator -= (const c &b) { return *this=*this-b; } c operator * (const c &b) { return c(r*b.r-i*b.i,r*b.i+b.r*i); } c operator *= (const c &b) { return *this=*this*b; } }a[N],b[N],x[N]; int l; int r[N]; void fft(c A[],int ty){ for(int i=0;i<l;i++) x[r[i]]=A[i]; for(int i=0;i<l;i++) A[i]=x[i]; for(int i=2;i<=l;i<<=1){ c wn(cos(pi*2/i),ty*sin(pi*2/i)); for(int j=0;j<l;j+=i){ c w(1,0); for(int k=j;k<j+i/2;k++){ c t=A[k+i/2]*w; A[k+i/2]=A[k]-t; A[k]+=t; w*=wn; } } } } int n,m; int main() { scanf("%d%d",&n,&m); for(int i=0;i<=n;i++) scanf("%lf",&a[i].r); for(int i=0;i<=m;i++) scanf("%lf",&b[i].r); l=1; while(l<=n+m) l<<=1; for(int i=0;i<l;i++) r[i]=(r[i>>1]>>1)|((i&1)*(l>>1)); fft(a,1);fft(b,1); for(int i=0;i<l;i++) a[i]*=b[i]; fft(a,-1); for(int i=0;i<n+m;i++) printf("%d ",int(a[i].r/l+0.5)); printf("%d",int(a[n+m].r/l+0.5)); return 0; }
[uoj#34] [洛谷P3803] 多項式乘法(FFT)