问题描述
给定一个长度为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
)升至O(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
)。但实际上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;
}