【BZOJ4836】二元運算

題目鏈接:BZOJ4836

解法:分治 + FFT

看到要求x+yxy 的方案數,第一反應是FFT。但題目有對於xy 的大小限制,故不能直接進行卷積出解,考慮分治。

假設要求[l,r] 區間內的數對答案的貢獻s ,則分別求出[l,m][m+1,r] 區間對答案的貢獻。而由於序列的生成函數Flm 中所有數(是次數不是係數)均小於G(m+1)r ,所以可以直接進行卷積,GlmF(m+1)r 同理。

遞歸處理即可。

(Tips:常數極大,本地跑的時候先等十秒,一開始誤以爲死循環調了好久。。)

代碼

#include<iostream>
#include<cstdio>
#include<complex>
#include<cmath>
#include<algorithm>
#include<cstring>

using namespace std;

complex<double> F[131073],G[131073],H[131073],P[131073],Q[131073];
double Pi=acos(-1.L);
int x,n,m,q,len,rev[131073],T,bit;

inline void fft(complex<double> *f,double check){
    for(register int i=0;i<len;++i)if(i<rev[i])swap(f[i],f[rev[i]]);
    for(register int i=1;i<len;i<<=1){
        complex<double> wn(cos(Pi/i),check*sin(Pi/i));
        for(int j=0;j<len;j+=(i<<1)){
            complex<double> w(1.L,0.L);
            for(register int k=0;k<i;++k){complex<double> x=f[j+k],y=w*f[i+j+k];f[j+k]=x+y,f[i+j+k]=x-y,w*=wn;}
        }
    }
    if(check<0.L)for(int i=0;i<len;++i)f[i]/=(double)len;
}

void work(int l,int r){
    if(l==r)return;
    int m=l+r>>1;
    work(l,m),work(m+1,r);
    len=1,bit=0;while(len<=r-l+1)len<<=1,++bit;for(register int i=0;i<len;++i)rev[i]=((rev[i>>1]>>1)|((i&1)<<bit-1));
    for(register int i=l;i<=m;++i)P[i-l]=F[i];for(register int i=m-l+1;i<len;++i)P[i]=complex<double>();
    for(register int i=m+1;i<=r;++i)Q[i-m-1]=G[i];for(register int i=r-m;i<len;++i)Q[i]=complex<double>();
    fft(P,1.L),fft(Q,1.L);
    for(register int i=0;i<len;++i)P[i]*=Q[i];
    fft(P,-1.L);
    for(register int i=0;i<=r-l+1;++i)H[i+l+m+1]+=P[i];
}

int main(){
    scanf("%d",&T);
    for(register int i=1;i<=T;++i){
        memset(F,0,sizeof(F)),memset(G,0,sizeof(G)),memset(H,0,sizeof(H));
        scanf("%d%d%d",&n,&m,&q);
        for(register int i=1;i<=n;++i)scanf("%d",&x),F[x]+=1.L;
        for(register int i=1;i<=m;++i)scanf("%d",&x),G[x]+=1.L;
        len=131072,bit=17;for(register int i=0;i<len;++i)rev[i]=((rev[i>>1]>>1)|((i&1)<<bit-1));
        reverse(G,G+50001),fft(F,1.L),fft(G,1.L);
        for(register int i=0;i<len;++i)H[i]=F[i]*G[i];
        fft(F,-1.L),fft(G,-1.L),fft(H,-1.L);
        for(register int i=0;i<=50000;++i)H[i]=H[i+50000],H[i+50000]=complex<double>();
        reverse(G,G+50001),work(0,50000);
        for(register int i=1;i<=q;++i)scanf("%d",&x),printf("%lld\n",(long long)round(H[x].real()));
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章