拆系数FFT及其部分优化

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

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

模版题:任意模数NTT


三模数NTT

常数巨大,跑的极慢


拆系数FFT

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

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

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

$ C=\sum\limits_{i=0}^{m-1}(Q_i>>15)x^i \ \ D=\sum\limits_{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('\n')
#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('\n');}
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;
}

 

posted @ 2019-01-08 21:07  Kananix  阅读(563)  评论(3编辑  收藏  举报

Contact with me