zoukankan      html  css  js  c++  java
  • 任意模数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]);
    }
    
  • 相关阅读:
    智能实验室-杀马(Defendio) 4.12.0.800
    智能实验室-结构化存储浏览器(SSExplorer) 1.7.0.170
    智能实验室-全能优化(Guardio) 4.94.0.830
    智能实验室-全能优化(Guardio) 4.9.0.790
    IT餐馆—第二十二回 控件
    当DiscuzNT遇上了Loadrunner(中)
    在Discuz!NT中进行缓存分层(本地缓存+memcached)
    介绍三个Silverlight 在线编辑器控件
    玩玩负载均衡在window与linux下配置nginx
    IT餐馆—第十八回 祭奠
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/13090885.html
Copyright © 2011-2022 走看看