任意模數FTT

模板題luogu4245

9次DFT

  • 由於在一般的條件下值域大概在102310^{23}下,所以找到三個NTT模數,它們的乘積大於102310^{23},求出三個模數下的答案,再用中國剩餘定理把它們合併到一起,變成模三個數的乘積下的答案,這就是它的實際答案。
  • 一共需要9次DFT,常數比較小,但是9次實在是太慢了。

三次變兩次

  • 由於複數域的神奇性質,我們在FFT的時候可以將計算C(x)=A(x)B(x)C(x)=A(x)B(x)這個原本需要三次DFT的操作變成只需要兩次。
  • F(x)=A(x)+iB(x),G(x)=A(x)iB(x)F(x)=A(x)+iB(x),G(x)=A(x)-iB(x)F(x)G(x)F(x)和G(x)是共軛的,那麼DFTF(x)DFTG(x)DFT_F(x)和DFT_G(x)也是共軛的。
  • DFTG(x)=conj(DFTF(nx))DFT_G(x)=conj(DFT_F(n-x)),這裏記conj(x)conj(x)表示xx的共軛複數((a+bi)(a+bi)的共軛複數是(abi)(a-bi))。
  • 有了這個我們只需要算出DFTF(x)DFT_F(x)就能求出DFTG(x)DFT_G(x)了,那麼就可以解出DFTA(x)DFTB(x)DFT_A(x)和DFT_B(x)了,省去了一次DFT。

應用到拆係數FFT

  • A(x)=aW+b,B(x)=cW+dA(x)=aW+b,B(x)=cW+d
  • A(x)B(x)=acW2+(ad+bc)W+bdA(x)B(x)=acW^2+(ad+bc)W+bd
  • 我們可以這樣求ac,ad,bc,bdac,ad,bc,bd
    P(x)=(a+ib)(c+id)=(acbd)+(ad+bc)iP(x)=(a+ib)(c+id)=(ac-bd)+(ad+bc)i
    Q(x)=(aib)(c+id)=(ac+bd)+(adbc)iQ(x)=(a-ib)(c+id)=(ac+bd)+(ad-bc)i
  • (a+ib)(aib)(a+ib)和(a-ib)的點值可以一次求出,(c+id)(c+id)也可以一次求出,再IDFT求出P(x)Q(x)P(x)和Q(x),最後就可以解出每一個位置ac,bd,ad,bcac,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]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章