模板題luogu4245
9次DFT
- 由於在一般的條件下值域大概在1023下,所以找到三個NTT模數,它們的乘積大於1023,求出三個模數下的答案,再用中國剩餘定理把它們合併到一起,變成模三個數的乘積下的答案,這就是它的實際答案。
- 一共需要9次DFT,常數比較小,但是9次實在是太慢了。
三次變兩次
- 由於複數域的神奇性質,我們在FFT的時候可以將計算C(x)=A(x)B(x)這個原本需要三次DFT的操作變成只需要兩次。
- 設F(x)=A(x)+iB(x),G(x)=A(x)−iB(x),F(x)和G(x)是共軛的,那麼DFTF(x)和DFTG(x)也是共軛的。
- 即DFTG(x)=conj(DFTF(n−x)),這裏記conj(x)表示x的共軛複數((a+bi)的共軛複數是(a−bi))。
- 有了這個我們只需要算出DFTF(x)就能求出DFTG(x)了,那麼就可以解出DFTA(x)和DFTB(x)了,省去了一次DFT。
應用到拆係數FFT
- A(x)=aW+b,B(x)=cW+d
- A(x)B(x)=acW2+(ad+bc)W+bd
- 我們可以這樣求ac,ad,bc,bd:
P(x)=(a+ib)(c+id)=(ac−bd)+(ad+bc)i
Q(x)=(a−ib)(c+id)=(ac+bd)+(ad−bc)i
- (a+ib)和(a−ib)的點值可以一次求出,(c+id)也可以一次求出,再IDFT求出P(x)和Q(x),最後就可以解出每一個位置ac,bd,ad,bc的值了。
- PS:爲了保證精度要用long double,我的方法常數還是比較大的。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 500005
#define db long double
#define ll long long
using namespace std;
const db Pi=acos(-1.0);
int n,m,i,j,k,a[maxn],b[maxn],lim,bt[maxn],mo;
ll H[maxn],W[maxn];
int I(db x){return (x<0)?-1:1;}
struct Z{
db x,y;
Z(db _x=0,db _y=0){x=_x,y=_y;}
} A[maxn],B[maxn],C[maxn],F[maxn],G[maxn],w[maxn];
Z operator+(Z a,Z b){return Z(a.x+b.x,a.y+b.y);}
Z operator*(Z a,Z b){return Z(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
Z operator-(Z a,Z b){return Z(a.x-b.x,a.y-b.y);}
void dft(Z *a,int sig){
for(int i=0;i<lim;i++) if (i<bt[i])
swap(a[i],a[bt[i]]);
for(int mid=1;mid<lim;mid<<=1){
Z Wi(cos(Pi/mid),sig*sin(Pi/mid));
for(int j=0;j<lim;j+=mid<<1){
Z w(1,0);
for(int k=0;k<mid;k++,w=w*Wi){
Z x=a[j+k],y=a[j+k+mid]*w;
a[j+k]=x+y,a[j+k+mid]=x-y;
}
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&mo);
for(i=0;i<=n;i++) scanf("%d",&a[i]),a[i]%=mo;
for(i=0;i<=m;i++) scanf("%d",&b[i]),b[i]%=mo;
for(lim=1;lim<=n+m;lim<<=1);
for(i=1;i<lim;i++) bt[i]=(bt[i>>1]>>1)|((i&1)*(lim>>1));
for(i=0;i<=n;i++) A[i].x=a[i]>>15,A[i].y=a[i]&((1<<15)-1);
for(i=0;i<=m;i++) B[i].x=b[i]>>15,B[i].y=b[i]&((1<<15)-1);
dft(A,1),dft(B,1);
for(i=1;i<lim;i++) C[i]=A[lim-i],C[i].y=-C[i].y;
C[0]=A[0],C[0].y=-C[0].y;
for(i=0;i<lim;i++) F[i]=A[i]*B[i],G[i]=C[i]*B[i];
dft(F,-1),dft(G,-1);
ll K=(1<<15)%mo,K2=(1<<30)%mo;
for(i=0;i<lim;i++){
ll x=(abs(F[i].x/lim)+0.5)*I(F[i].x),xx=(abs(F[i].y/lim)+0.5)*I(F[i].y);
ll y=(abs(G[i].x/lim)+0.5)*I(G[i].x),yy=(abs(G[i].y/lim)+0.5)*I(G[i].y);
H[i]=(((x+y)/2%mo*K2+xx%mo*K+(y-x)/2)%mo+mo)%mo;
}
for(i=0;i<=n+m;i++) printf("%lld ",H[i]);
}