BZOJ 3509 分块FFT

思路:

跟今年WC的题几乎一样 (但是这道题有重 不能用bitset水过去)

正解:分块FFT

http://blog.csdn.net/geotcbrl/article/details/50636401    from GEOTCBRL 

可以看看hgr的题解..写得很详细

//By SiriusRen
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const double pi=acos(-1);
const int N=100050;
int n,nn,num[N],R[N],L,Block,block[N],cnt[50][N];
long long ans;
struct Complex{
    double x,y;Complex(){}
    Complex(double X,double Y){x=X,y=Y;}
}A[N],B[N],C[N];
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);}
Complex operator/(Complex a,int b){return Complex(a.x/b,a.y/b);}
void FFT(Complex *a,int f){
    for(int i=0;i<n;i++)if(i<R[i])swap(a[i],a[R[i]]);
    for(int i=1;i<n;i<<=1){
        Complex wn=Complex(cos(pi/i),f*sin(pi/i));
        for(int j=0;j<n;j+=(i<<1)){
            Complex w=Complex(1,0);
            for(int k=0;k<i;k++,w=w*wn){
                Complex x=a[j+k],y=w*a[j+k+i];
                a[j+k]=x+y,a[j+k+i]=x-y;
            }
        }
    }
    if(!~f)for(int i=0;i<n;i++)a[i]=a[i]/n;
}
int main(){
    scanf("%d",&nn);
    for(int i=1;i<=nn;i++)scanf("%d",&num[i]);
    for(n=1;n<=60000;n<<=1)L++;
    for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    Block=min(int(sqrt(nn)*10),nn);
    for(int i=1;i<=nn;i++)block[i]=(i-1)/Block+1;
    for(int i=1;i<=nn;i++)cnt[block[i]][num[i]]++;
    for(int I=1;I<=block[nn];I++){
        int L=lower_bound(block+1,block+1+nn,I)-block,R=upper_bound(block+1,block+1+nn,I)-block-1;
        for(int j=L;j<=R;j++){
            cnt[I][num[j]]--;
            for(int i=L;i<j;i++)
                if(num[j]*2-num[i]>=0)ans+=cnt[I][num[j]*2-num[i]];
        }
    }
    for(int i=1;i<=nn;i++)cnt[0][num[i]]++;
    for(int I=1;I<=block[nn];I++){
        int L=lower_bound(block+1,block+1+nn,I)-block,R=upper_bound(block+1,block+1+nn,I)-block-1;
        for(int i=L;i<=R;i++)cnt[0][num[i]]--;
        for(int j=L;j<=R;j++)
            for(int i=L;i<j;i++)
                if(num[j]*2-num[i]>=0)ans+=cnt[0][num[j]*2-num[i]];
    }
    for(int i=1;i<=nn;i++)cnt[0][num[i]]++;
    for(int I=block[nn];I;I--){
        int L=lower_bound(block+1,block+1+nn,I)-block,R=upper_bound(block+1,block+1+nn,I)-block-1;
        for(int i=L;i<=R;i++)cnt[0][num[i]]--;
        for(int k=L;k<=R;k++)
            for(int j=k-1;j>=L;j--)
                if(num[j]*2-num[k]>=0)ans+=cnt[0][num[j]*2-num[k]];
    }
    for(int I=1;I<=block[nn];I++){
        for(int i=0;i<n;i++)A[i].x=A[i].y=B[i].x=B[i].y=0;
        int L=lower_bound(block+1,block+1+nn,I)-block,R=upper_bound(block+1,block+1+nn,I)-block-1;
        for(int i=1;i<L;i++)A[num[i]].x++;
        for(int i=R+1;i<=nn;i++)B[num[i]].x++;
        FFT(A,1),FFT(B,1);
        for(int i=0;i<n;i++)C[i]=A[i]*B[i];
        FFT(C,-1);
        for(int i=L;i<=R;i++)ans+=(long long)(C[num[i]*2].x+0.2);
    }
    printf("%lld\n",ans);
}
分块FFT哦~

 

转载于:https://www.cnblogs.com/SiriusRen/p/6532866.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值