【BZOJ】2194: 快速傅立叶之二

【题意】请计算C[k]=sigma(a[i]*b[i-k]) 其中 k < = i < n ,并且有 n < = 10 ^ 5。 a,b中的元素均为小于等于100的非负整数。

【算法】快速傅里叶变换(FFT)处理卷积

【题解】题目要求:

$$C_k=\sum_{i=k}^{n-1}A_i*B_{i-k}$$

令B'为B的数组反转,转化成和为定值的形式,即:

$$C_k=\sum_{i=k}^{n-1}A_i*B'_{n+k-1-i}$$

因为:

$$A_i=0,i\in[n,n+k-1]$$

$$B_{n+k-1-i}=0,i\in[0,k-1]$$

所以可以扩展式子的上下界,即:

$$C_k=D_{n+k-1}=\sum_{i=0}^{n+k-1}A_i*B'_{n+k-1-i}$$

这就可以用FFT处理卷积了。

复杂度O(n log n)。

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=300010;
const double PI=acos(-1);
int n;
struct cp{
    double x,y;
    cp(double a,double b){x=a;y=b;}
    cp(){x=y=0;}
    cp operator + (cp a){return (cp){x+a.x,y+a.y};}
    cp operator - (cp a){return (cp){x-a.x,y-a.y};}
    cp operator * (cp a){return (cp){x*a.x-y*a.y,x*a.y+y*a.x};}
}a[maxn],b[maxn];
void fft(cp *a,int n,int f){
    int k=0;
    for(int i=0;i<n;i++){
        if(i>k)swap(a[i],a[k]);
        for(int j=n>>1;(k^=j)<j;j>>=1);
    }
    for(int l=2;l<=n;l<<=1){
        int m=l>>1;
        cp wn=(cp){cos(2*PI*f/l),sin(2*PI*f/l)};
        for(cp *p=a;p!=a+n;p+=l){
            cp w=(cp){1,0};
            for(int i=0;i<m;i++){
                cp t=p[i+m]*w;
                p[i+m]=p[i]-t;
                p[i]=p[i]+t;
                w=w*wn;
            }
        }
    }
    if(f==-1){for(int i=0;i<n;i++)a[i].x/=n;}
}
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++)scanf("%lf %lf",&a[i].x,&b[n-i-1].x);
    int N=1;while(N<n*2)N*=2;
    fft(a,N,1);fft(b,N,1);
    for(int i=0;i<N;i++)a[i]=a[i]*b[i];//2n
    fft(a,N,-1);
    for(int i=n-1;i<2*n-1;i++)printf("%d\n",(int)(a[i].x+0.1));
    return 0;
}
View Code

 

 

只要和为定值的形式就可以卷积,如果看着很奇怪就将其中一个数组下标变成i,然后扩展上下界。

posted @ 2018-02-05 16:17  ONION_CYC  阅读(284)  评论(0编辑  收藏  举报