【bzoj4836】二元運算(cdq分治+FFT)

題目鏈接
看到題面就不難想到是FFT的題
對兩個序列各開一個桶,分別爲A和B
yx 時,不難想到把A反轉一下,然後把A和B做一個卷積就好了。
x<y 時,我們可以考慮cdq分治,合併區間把A在左區間的部分和B在右區間的部分做個卷積並累加就好了。
如果有誤在評論區吼一聲哦!
具體的話請看代碼:

#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
const double pi=acos(-1.0);
int T,n,m,q,rev[200010],t1[50010],t2[50010];
struct cmplx{
    double r,i;
    cmplx(double a=0,double b=0){r=a;i=b;}
    cmplx operator+(cmplx b){
        return cmplx(r+b.r,i+b.i);
    }
    cmplx operator+=(cmplx b){
        *this=*this+b;
        return *this;
    }
    cmplx operator++(){
        r++;
        return *this;
    }
    cmplx operator-(cmplx b){
        return cmplx(r-b.r,i-b.i);
    }
    cmplx operator*(cmplx b){
        return cmplx(r*b.r-i*b.i,r*b.i+i*b.r);
    }
    cmplx operator*=(cmplx b){
        *this=*this*b;
        return *this;
    }
}a[200010],b[200010],c[200010],x[200010],y[200010];
int rd(){
    int x=0;
    char c;
    do c=getchar();
    while(!isdigit(c));
    do{
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }while(isdigit(c));
    return x;
}
void FFT(cmplx *a,int limit,int type){
    for(int i=0;i<limit;i++)
        if(i<rev[i])
            swap(a[i],a[rev[i]]);
    for(int mid=1;mid<limit;mid<<=1){
        cmplx wn=cmplx(cos(pi/mid),type*sin(pi/mid));
        for(int i=0;i<limit;i+=(mid<<1)){
            cmplx w=cmplx(1,0);
            for(int j=0;j<mid;j++,w*=wn){
                cmplx x=a[i+j],y=w*a[i+j+mid];
                a[i+j]=x+y;
                a[i+j+mid]=x-y;
            }
        }
    }
    if(type==-1)
        for(int i=0;i<limit;i++)
            a[i].r/=limit;
    return;
}
void cdq(int l,int r){
    if(l==r)
        return;
    int mid=(l+r)>>1;
    cdq(l,mid);
    cdq(mid+1,r);
    int limit=1,len=0;
    while(limit<=r-l+1){
        limit<<=1;
        len++;
    }
    for(int i=l;i<=mid;i++)
        x[i-l]=t1[i];
    for(int i=mid+1;i<=r;i++)
        y[i-mid-1]=t2[i];
    for(int i=mid-l+1;i<limit;i++)
        x[i]=cmplx(0,0);
    for(int i=r-mid;i<limit;i++)
        y[i]=cmplx(0,0);
    for(int i=0;i<limit;i++)
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));
    FFT(x,limit,1);
    FFT(y,limit,1);
    for(int i=0;i<limit;i++)
        x[i]*=y[i];
    FFT(x,limit,-1);
    for(int i=0;i<=r-l+1;i++)
        c[i+l+mid+1]+=x[i];
    return;
}
int main(){
    scanf("%d",&T);
    while(T--){
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(t1,0,sizeof(t1));
        memset(t2,0,sizeof(t2));
        n=rd();m=rd();q=rd();
        for(int i=1;i<=n;i++){
            int x=rd();
            t1[x]++;
            a[x]+=1;
        }
        for(int i=1;i<=m;i++){
            int x=rd();
            t2[x]+=1;
            b[50000-x]+=1;
        }
        int limit=1,l=0;
        while(limit<=100000){
            limit<<=1;
            l++;
        }
        for(int i=0;i<limit;i++)
            rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
        FFT(a,limit,1);
        FFT(b,limit,1);
        for(int i=0;i<limit;i++)
            c[i]=a[i]*b[i];
        FFT(c,limit,-1);
        for(int i=0;i<=50000;i++)
            c[i]=c[i+50000];
        for(int i=50001;i<=100000;i++)
            c[i]=0;
        cdq(0,50000);
        while(q--){
            int ci=rd();
            printf("%lld\n",(long long)(c[ci].r+0.5));
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章