【BZOJ4836】二元运算

题目链接:BZOJ4836

解法:分治 + FFT

看到要求 x + y x y 的方案数,第一反应是FFT。但题目有对于 x y 的大小限制,故不能直接进行卷积出解,考虑分治。

假设要求 [ l , r ] 区间内的数对答案的贡献 s ,则分别求出 [ l , m ] [ m + 1 , r ] 区间对答案的贡献。而由于序列的生成函数 F l m 中所有数(是次数不是系数)均小于 G ( m + 1 ) r ,所以可以直接进行卷积, G l m F ( 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()));
    }
}

猜你喜欢

转载自blog.csdn.net/ezoixx174/article/details/81572064