zoukankan      html  css  js  c++  java
  • FFT入门及其应用

    系列博客 https://www.cnblogs.com/2016gdgzoi471/p/9476883.html 

    FFT原理详解,包含大数乘法的代码

    https://www.cnblogs.com/RabbitHu/p/FFT.html

    多项式乘法:就是套一套板子:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    #define ll long long
    #define N 2000005
    
    struct complex{
        double x,y;
        complex (double xx=0,double yy=0){x=xx,y=yy;}
    };
    complex operator + (complex a,complex b){ return complex(a.x+b.x , a.y+b.y);}
    complex operator - (complex a,complex b){ return complex(a.x-b.x , a.y-b.y);}
    complex operator * (complex a,complex b){ return complex(a.x*b.x-a.y*b.y , a.x*b.y+a.y*b.x);}
    
    typedef complex cp;
    const double pi = acos(-1.0);
    inline int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    ll A[N],B[N],lena,lenb,n,res[N];
    cp a[N],b[N],omg[N],inv[N];
    
    void init(){//预处理出单位根及其倒数 
        for(register int i=0;i<n;i++)
            omg[i]=cp(cos(2*pi/n*i),sin(2*pi/n*i));
        for(register int i=0;i<n;i++)
            inv[i]=cp(cos(2*pi/n*i),-sin(2*pi/n*i));
    }
    
    void fft(cp *a, cp *omg){
        int lim = 0;
        while((1<<lim)<n)lim++;    //向下分治的次数 
        for(register int i=0;i<n;i++){    //先把系数移到最后一层递归的位置 
            int t=0;
            for(register int j=0;j<lim;j++)
                if((i>>j) & 1) t|=(1<<(lim-j-1));
            if(i<t)swap(a[i],a[t]);
        }
        for(register int l=2;l<=n;l*=2){    //每个递归区间的长度 
            int m=l/2;
            for(cp *p=a;p!=a+n;p+=l)
                for(register int i=0;i<m;i++){//蝴蝶操作 
                    cp t=omg[n/l*i]*p[i+m];
                    p[i+m]=p[i]-t;
                    p[i]=p[i]+t; 
                }
        }
        
    }
    
    //a0,a1,a2,a3...an
    
    int main(){
        lena=read();lenb=read();
        lena++;lenb++;
        n=1;while(n<lena+lenb)n<<=1;
        for(register int i=0;i<lena;i++)A[i]=read();
        for(register int i=0;i<lenb;i++)B[i]=read();
        for(register int i=0;i<lena;i++)a[i]=cp(A[i],0);
        for(register int i=0;i<lenb;i++)b[i]=cp(B[i],0);
        init();
        fft(a,omg);fft(b,omg);
        for(register int i=0;i<n;i++)a[i]=a[i]*b[i];
        fft(a,inv);
            
        for(register int i=0;i<n;i++)
            res[i]=floor(a[i].x/n+0.5);
        for(register int i=0;i<lena+lenb-1;i++)
            cout<<res[i]<<" ";
            
    }
    View Code

    多项式乘法的卷积形式

    https://www.cnblogs.com/RabbitHu/p/BZOJ2194.html

    FFT在字符串相似度匹配上的应用:

    https://blog.csdn.net/qq_31759205/article/details/80060918

    例题:

    GYM101667H 题解: https://blog.csdn.net/ACTerminate/article/details/79325574

    #include<bits/stdc++.h>
    #include<complex>
    using namespace std;
    #define ll long long 
    #define N 400005
    typedef complex<double> cp;
    
    int lena,lenb;
    char A[N],B[N];
    
    cp a[N],b[N],omg[N],inv[N];
    int n,sum[N],F[N];
    const double pi = acos(-1.0);
    void init(){
        for(int i=0;i<n;i++){
            omg[i]=cp(cos(2*pi/n*i),sin(2*pi/n*i));
            inv[i]=conj(omg[i]);
        }
    }
    void fft(cp *a, cp * omg){
        int lim=0;
        while((1<<lim)<n)lim++;
        for(int i=0;i<n;i++){
            int t=0;
            for(int j=0;j<lim;j++)
                if((i>>j) & 1) t|=(1<<(lim-j-1));
            if(i<t)swap(a[i],a[t]);    
        }
        for(int l=2;l<=n;l*=2){
            int m=l/2;
            for(cp *p=a;p!=a+n;p+=l){
                for(int i=0;i<m;i++){
                    cp t=omg[n/l*i]*p[i+m];
                    p[i+m]=p[i]-t;
                    p[i]=p[i]+t;
                }
            }
        }
    }
        
    void calc(char ch){
        memset(a,0,sizeof a);
        memset(b,0,sizeof b);
        for(int i=0;i<lena;i++)
            if(A[i]==ch) a[i].real(1);
            else a[i].real(0);
        for(int i=0;i<lenb;i++)
            if(B[i]==ch) b[i].real(1);
            else b[i].real(0);
        fft(a,omg);fft(b,omg);
        for(int i=0;i<n;i++)
            a[i]=a[i]*b[i];
        fft(a,inv);
        for(int i=0;i<n;i++)
            sum[i]=floor(a[i].real()/n+0.5);
        for(int i=0;i<lena+lenb-1;i++)
            F[i]+=sum[i];
    } 
    
    int main(){
        cin>>lena>>lenb;
        n=1;
        while(n<lena+lenb)n<<=1;
        init();
        scanf("%s%s",A,B);
        
        for(int i=0;i<lena;i++){
            if(A[i]=='S')A[i]='R';
            else if(A[i]=='R')A[i]='P';
            else if(A[i]=='P')A[i]='S';
        }
        for(int i=0,j=lenb-1;i<j;i++,j--)
            swap(B[i],B[j]);
            
        //对每种字符单独求,再加起来 
        calc('R');calc('P');calc('S');
        
        //F[k]=sum{A[i]*B[k-i]},表示从A[k-(lenb-1)]开始匹配的相似度 
        //因为是匹配一整个B串,所以k>=lenb-1 
        int Max=0;
        for(int i=lenb-1;i<lena+lenb-1;i++)
            Max=max(Max,F[i]);
        cout<<Max<<'
    ';
    } 
    View Code

    2018宁夏网络赛H题:https://nanti.jisuanke.com/t/A1676

    题解:https://blog.csdn.net/qq_38891827/article/details/80281151

    一份不错的板子:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<complex>
    using namespace std;
    #define maxn 2000005
    
    const double pi=acos(-1.0);
    
    typedef complex<double>cp;
    cp a[maxn],b[maxn];
    
    int S,T,n,m,L,R[maxn],ans[maxn];
    long long F[maxn];
    char s[maxn],t[maxn];
    double f[maxn],g[maxn];
    
    void FFT(cp a[maxn],int opt)
    {
        int lim = 0;
        while((1 << lim) < n) lim++;
        for(register int i = 0; i < n; i++){
            int t = 0;
            for(register int j = 0; j < lim; j++)
                if((i >> j) & 1) t |= (1 << (lim - j - 1));
            if(i < t) swap(a[i], a[t]); // i < t 的限制使得每对点只被交换一次(否则交换两次相当于没交换)
        }
        for (int k=1;k<n;k<<=1)
        {
            cp wn=cp(cos(pi/k),opt*sin(pi/k));
            for (int i=0;i<n;i+=(k<<1))
            {
                cp w=cp(1,0);
                for (int j=0;j<k;++j,w=w*wn)
                {
                    cp x=a[i+j],y=w*a[i+j+k];
                    a[i+j]=x+y,a[i+j+k]=x-y;
                }
            }
        }
    }
    void calc(char ch1,char ch2,char ch3)
    {
        memset(a,0,sizeof a);
        memset(b,0,sizeof b);
        for(int i=0;i<T;i++)
            if(t[i]==ch2 || t[i]==ch3)a[i].real(1);
        for(int i=0;i<S;i++)
            if(s[i]==ch1)b[i].real(1);
        
        FFT(a,1);FFT(b,1);
        for (int i=0;i<=n;++i) a[i]=a[i]*b[i];
        FFT(a,-1);
        for (int i=S-1;i<T;++i)
        {
             F[i]+=(long long)(a[i].real()/n+0.5);//对于每种匹配位置累计赢的次数
        }
    }
    int main()
    {
        scanf("%s%s",t,s);//S短,T长
        S=strlen(s);
        T=strlen(t);
        for (int i=0;i<S/2;++i) swap(s[i],s[S-i-1]);//短串逆置
    
        m=S+T-2;
        for (n=1;n<=m;n<<=1) ++L;
    
            
        calc('S','P','L');
        calc('P','R','K');
        calc('R','L','S');
        calc('L','K','P');
        calc('K','S','R');
    
        long long  ans=0;
        for (int i=S-1;i<T;++i)//这个范围自己考虑一下就好了
          ans=max(ans,F[i]);//所有位置取max
        printf("%lld
    ",ans);
    }
    View Code

     bzoj4259:同样是逆置字符串的思路,还多了一个分段求FFT的思路

    但是bzoj上不能交了https://www.cnblogs.com/clrs97/p/4814499.html

    16香港网络赛:FFT求A[i]+A[j]=A[k]的对数,FFT+容斥+坐标偏移

    题解:https://blog.csdn.net/qq_38891827/article/details/80281151

    /*
    F[k]=sum{a[i]*b[k-i]}
    由于本题要把坐标向右偏移LOW,得
        F[k+2*LOW]=sum{a[i+LOW]*b[k-i+LOW]}
    
    枚举每个数A[i]    
        那么F[A[i]+2*LOW]就表示x^A[i]出现的次数 
    再容斥掉一些答案:
        1.自身*自身:F[(A[i]+LOW)*2]--;
        2.A[i]!=0,每当A[j]=0时:
            所有(i,j,i),(j,i,i)都会被算贡献,所以减掉0的个数 
        3.A[i]=0,每当A[j]=0的情况:
            所有(i,j,i),(j,i,i)都会被算贡献,所以减掉0的个数-1 
    */
    #include<bits/stdc++.h>
    #include<complex>
    using namespace std;
    #define ll long long 
    #define N 1000005
    #define LOW 50000
    
    ll F[N],m,A[N],cnt[N],n;
    typedef complex<double> cp;
    cp a[N],b[N];
    const double pi = acos(-1.0);
    
    void FFT(cp a[N], int opt){
        int lim=0;
        while((1<<lim)<n)lim++;
        for(int i=0;i<n;i++){
            int t=0;
            for(int j=0;j<lim;j++)
                if((i>>j) & 1)t|=(1<<(lim-j-1));
            if(i<t)swap(a[i],a[t]);
        }
        for(int k=1;k<n;k<<=1){
            cp wn=cp(cos(pi/k),opt*sin(pi/k));//原根 
            for(int i=0;i<n;i+=(k<<1)){
                cp w=cp(1,0);
                for(int j=0;j<k;j++,w=w*wn){
                    cp x=a[i+j],y=w*a[i+j+k];
                    a[i+j]=x+y;
                    a[i+j+k]=x-y;
                }
            }
        }
    }
    
    int zero;
    int main(){
        cin>>m;
        for(int i=1;i<=m;i++){
            scanf("%lld",&A[i]);
            cnt[A[i]+LOW]++;
            if(A[i]==0)zero++;
        }
        n=1;
        while(n<200000)n<<=1;
        for(int i=0;i<n;i++)
            a[i].real(cnt[i]),b[i].real(cnt[i]);
            
        FFT(a,1);FFT(b,1);
        for(int i=0;i<n;i++)a[i]*=b[i];
        FFT(a,-1);
        
        for(int i=0;i<n;i++)F[i]=(ll)(a[i].real()/n+0.5);
        for(int i=1;i<=m;i++)F[(A[i]+LOW)*2]--;
        ll ans=0;
        for(int i=1;i<=m;i++){//每个数出现的次数 
            ans+=F[A[i]+LOW*2];
            //减去0的影响 
            if(A[i]==0)
                ans-=(zero-1)*2;
            else ans-=zero*2;
        }
        
        cout<<ans<<'
    ';
    } 
    View Code
  • 相关阅读:
    css实现导航栏切换动画
    ubuntu系统下mysql重置密码和修改密码操作
    Ubuntu16.04 安装配置nginx,实现多项目管理、负载均衡
    每天一点点之数据结构与算法
    vuex基本使用
    在 npm 中如何用好 registry
    django模板
    skywalking 通过python探针监控Python 微服务应用性能
    Centos7新加磁盘扩容根分区
    python3中用HTMLTestRunner.py报ImportError: No module named 'StringIO'如何解决
  • 原文地址:https://www.cnblogs.com/zsben991126/p/12304340.html
Copyright © 2011-2022 走看看