[CodeChef] COUNTARI

问题描述

给定一个长度为N的数组A[],求有多少对i, j, k(1<=i

输入格式

第一行一个整数N(N<=10^5)。
接下来一行N个数A[i](A[i]<=30000)。

输出格式

一行一个整数。

样例输入

10
3 5 3 6 3 4 10 4 5 2

样例输出

9


题解

PS:之后讲的“中间数”即为A[k]-A[j]=A[j]-A[i]时的A[j]
显然,对于每个数A[i],如果我们把A[1]至A[i-1]的值放入大小为30000的数组c1[]中(即,c1[j]记录前i-1个数中有多少个值为j的数),把A[i+1]至A[n]的值放入c2[]中,那么,c1和c2进行FFT之后,c3[2*A[i]]即为以i 为中间数的答案(因为c3[2*a[i]]= c1[l]*c2[k](l+k==2*A[i]))。
这个东东很优秀。我们可以把时间复杂度由O(n 2 )升至O(n 2 l o g 2 n )。。。
想想,为什么进行优秀的FFT之后会变慢?因为每次FFT之后我们都只用了一个位置的数!!!
也就是说,我们要在每次FFT之后多获取一点信息。
在老板的提示下,使用分块。
我们定每块的长度为s,将数列分为n/s块。
对于每个数a[i],答案可能的区域被分为:
这里写图片描述
1表示块外左边,2表示块内左边,3表示块内右边,4表示块外右边。
所以,将A[i]设为中间数,另外两数的范围有(1,3)、(1,4)、(2,3)、(2,4)。
(1,4)可以用FFT搞,其它三个都可以用正常分块思想搞出来。
总时间复杂度O(n*s+Max_v l o g 2 M a x V )。但实际上FFT常数较大,每块长度最好大一点。
PS:坑?
1、自己造的:FFT一定要好好打;
2、不要开二维数组,一个complex_double的空间是16B。。。会G


代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <complex>
#define db double
#define cd complex<db>
#define ll long long
using namespace std;
const double pi=3.1415926535897932;
cd w[70001];
ll n,s;
void fly(cd a[],ll f){
    ll i,j,k,m;
    for(i=j=0;i<n;i++){
        if(i<j)swap(a[i],a[j]);
        for(k=n>>1;(j^=k)<k;k>>=1);
    }
    w[0]=1;
    for(m=1;m<n;m<<=1){
        cd ha=exp(cd(0,pi*(db)f/(db)m));
        for(i=1;i<m;i++)w[i]=w[i-1]*ha;
        for(i=0;i<n;i+=(m<<1))
            for(j=0;j<m;j++){
                cd p=a[i+j],q=a[i+j+m]*w[j];
                a[i+j]=p+q,a[i+j+m]=p-q;
            }
    }
    if(f==1)return;
    cd tmp=1.0/(db)n;
    for(i=0;i<n;i++)
        a[i]*=tmp;
}
ll gg(cd x)
{return (ll)floor(x.real()+0.5);}
cd ti[70001];
void FFT(cd a[],cd b[],cd c[]){
    for(ll i=0;i<n;i++)ti[i]=b[i],c[i]=a[i];
    fly(c,1),fly(ti,1);
    for(ll i=0;i<n;i++)c[i]*=ti[i];
    fly(c,-1);
}
cd ge[70001],now[70001],tmp[70001];
ll v[100005];
int main()
{
    ll i,k,j,ans=0,all,maxc=0;
    scanf("%lld",&all);
    s=(ll)sqrt(all)*3;
    for(i=1;i<=all;i++)scanf("%lld",&v[i]),maxc=max(maxc,v[i]);
    for(n=1;n<=maxc*2;n<<=1);
    ll lb=(all-1)/s+1;
    for(i=1;i<=all;i++)now[v[i]]+=1;
    for(i=1;i<=lb;i++){
        for(j=(i-1)*s+1;j<=i*s&&j<=all;j++)
            now[v[j]]-=1;
        FFT(now,ge,tmp);
        for(j=(i-1)*s+1;j<=i*s&&j<=all;j++)
            for(k=j+1;k<=i*s&&k<=all;k++)
                if(2*v[j]-v[k]>=0)ans+=gg(ge[2*v[j]-v[k]]);
        for(j=(i-1)*s+1;j<=i*s&&j<=all;j++)
            ans+=gg(tmp[v[j]*2]);
        for(j=(i-1)*s+1;j<=i*s&&j<=all;j++)
            ge[v[j]]+=1;
    }
    for(i=0;i<=maxc;i++)now[i]=0;
    for(i=all;i;--i){
        ll nb=(i-1)/s+1;
        for(j=i-1;j>=(nb-1)*s+1;--j)
            if(2*v[i]-v[j]>=0)ans+=gg(now[2*v[i]-v[j]]);
        now[v[i]]+=1;
    }
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/includelhc/article/details/80013467