zoukankan      html  css  js  c++  java
  • 拆系数FFT及其部分优化

    模拟考某题一开始由于校内OJ太慢直接拆系数FFT跑不过

    后来被神仙婊了一顿之后发现复杂度写炸了改了改随便过

    模版题:任意模数NTT


    三模数NTT

    常数巨大,跑的极慢


    拆系数FFT

    原理是对于两个多项式$ P=sumlimits_{i=0}^{n-1}P_ix^i Q=sumlimits_{i=0}^{m-1}Q_ix^i$

    直接$ FFT$计算会发现值域达到$ 10^{23}$会炸精度

    $ A=sumlimits_{i=0}^{n-1}(P_i>>15)x^i B=sumlimits_{i=0}^{n-1}(P_i&32767)x^i$

    $ C=sumlimits_{i=0}^{m-1}(Q_i>>15)x^i D=sumlimits_{i=0}^{m-1}(Q_i&32767)x^i$

    我们只要求$ (A*C)<<30,(B*C+A*D)<<15,B*D$这三项的和即可

    设一次$ DFT/IDFT$为一次操作

    暴力实现需要进行$ 8$次操作


    精度问题 

    如果用$ k$次乘法计算$ w_n^k$会损失大量精度

    需要利用三角函数预处理单位根,这样可以用$ double$代替$ long double$


    优化

    参考myy的2016年集训队论文

    合并$DFT$

    设我们要计算$ DFT_A$和$DFT_B$

    令$$ P(x)=A(x)+iB(x) \ Q(x)=A(x)-iB(x)$$

    我们只要计算一次$ DFT_P$就可以推出$ DFT_Q$

    推导请参考CMXRYNP'S Blog

    $DFT_A[i]=frac{DFT_P[i]+DFT_Q[i]}{2}$

    $DFT_B[i]=frac{DFT_P[i]-DFT_Q[i]}{2i}$

    合并$IDFT$

    同理

    但这里甚至不需要求$ IDFT_Q$

    事实上$IDFT_P$的实部和虚部分别对应$ IDFT_A$和$IDFT_B$

    这样就把$ 8$次操作减少到$4$次了


    代码 

    #include<ctime>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #define l putchar('
    ')
    #define file(x)freopen(x".in","r",stdin);freopen(x".out","w",stdout)
    #define block 32768
    #define rt register int
    #define ll long long
    using namespace std;
    inline ll read(){
        ll x=0;char zf=1;char ch=getchar();
        while(ch!='-'&&!isdigit(ch))ch=getchar();
        if(ch=='-')zf=-1,ch=getchar();
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf;
    }
    void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
    void writeln(const ll y){write(y);putchar('
    ');}
    int k,m,n,x,y,z,cnt,ans,p;
    namespace any_module_NTT{
        vector<int>R;
        const double PI=acos(-1.0);
        struct cp{
            double x,y;
            cp operator +(const cp s)const{return {x+s.x,y+s.y};}
            cp operator -(const cp s)const{return {x-s.x,y-s.y};}
            cp operator *(const cp s)const{return {x*s.x-y*s.y,x*s.y+y*s.x};}
        }w[18][1<<17];
        void FFT(const int n,vector<cp>&A){
            A.resize(n);
            for(rt i=0;i<n;i++)if(i>R[i])swap(A[i],A[R[i]]);
            for(rt i=1,s=0;i<n;i<<=1,s++){
                for(rt j=0;j<n;j+=i<<1){
                    for(rt k=0;k<i;k++){
                        const register cp x=A[j+k],y=w[s][k]*A[i+j+k];
                        A[j+k]=x+y,A[i+j+k]=x-y;
                    }
                }
            }
        }
        vector<int>Mul(vector<int>&x,vector<int>&y){    
        
            int sz=x.size()+y.size()-1,lim=1;
            while(lim<=sz)lim<<=1;R.resize(lim);        
            for(rt i=0;(1<<i)<lim;i++)
            for(rt j=0;j<(1<<i);j++)w[i][j]={cos(PI*j/(1<<i)),sin(PI*j/(1<<i))};    
            vector<cp>AB(lim),CD(lim),AC(lim),BC(lim);
            for(rt i=1;i<lim;i++)R[i]=(R[i>>1]>>1)|(i&1)*(lim>>1);
            for(rt i=0;i<x.size();i++)AB[i].x=((ll)x[i])&32767,AB[i].y=((ll)x[i])>>15;
               for(rt i=0;i<y.size();i++)CD[i].x=((ll)y[i])&32767,CD[i].y=((ll)y[i])>>15;
               FFT(lim,AB);FFT(lim,CD);
            for(rt i=0;i<lim;i++){
                static cp na,nb,nc,nd;const int pl=(lim-1)&(lim-i);
                na=AB[i]+(cp){AB[pl].x,-AB[pl].y},nb=AB[i]-(cp){AB[pl].x,-AB[pl].y};
                nc=CD[i]+(cp){CD[pl].x,-CD[pl].y},nd=CD[i]-(cp){CD[pl].x,-CD[pl].y};
                const cp v1={0.5,0},v2={0,-0.5};
                na=na*v1;nb=nb*v2;nc=nc*v1;nd=nd*v2;
                AC[pl]=na*nc+na*nd*(cp){0,1};
                BC[pl]=nb*nc+nb*nd*(cp){0,1};        
            }
            FFT(lim,AC);FFT(lim,BC);
            vector<int>ans(sz);
            for(rt i=0;i<sz;i++){
                ll v1=AC[i].x/lim+0.5,v2=AC[i].y/lim+BC[i].x/lim+0.5,v3=BC[i].y/lim+0.5;
                ans[i]=(ll)((v3%p<<30)+(v2%p<<15)+v1)%p;
            }
            return ans;
        }
    }
    using namespace any_module_NTT;
    vector<int>A,B;
    int main(){
        n=read();A.resize(n+1);m=read();B.resize(m+1);p=read();
        for(rt i=0;i<=n;i++)A[i]=read();for(rt i=0;i<=m;i++)B[i]=read();
        A=Mul(A,B);
        for(rt i=0;i<=n+m;i++)write((A[i]+p)%p),putchar(' ');
        return 0;
    }
  • 相关阅读:
    计算机网络-TCP的三次握手与四次挥手
    计算机网络-XSS及CSRF攻击防御
    计算机网络-HTTP与HTTPS的区别
    装饰器模式和代理模式的区别
    23种设计模式总结
    单例模式详解
    常用设计模式总结
    PG-用户|角色管理
    PG-表空间管理
    TiDB-性能测试
  • 原文地址:https://www.cnblogs.com/DreamlessDreams/p/10241267.html
Copyright © 2011-2022 走看看