第一想法
\qquad 首先考虑暴力枚举,设 f [ i ] [ j ] f[i][j] f[i][j]表示以第 i i i个数为三元组的第一个数,以第 j j j个数表示三元组的最后一个数.那么每次只要重新从 i + 1 i+1 i+1开始统计每个数的个数,代入求出答案即可.
\qquad 然后我们再让 f [ i ] [ j ] + = f [ i ] [ j − 1 ] f[i][j]+=f[i][j-1] f[i][j]+=f[i][j−1],操作完后 f [ i ] [ j ] f[i][j] f[i][j]就表示为以第 i i i个数为三元组的第一个数,以第 i i i 到第 j j j 个数表示三元组的最后一个数的三元组总数.
\qquad 然后我们再让 f [ i ] [ j ] + = f [ i + 1 ] [ j ] f[i][j]+=f[i+1][j] f[i][j]+=f[i+1][j],操作完后 f [ i ] [ j ] f[i][j] f[i][j]就表示第 i i i个数到第 j j j个数的所有三元组的个数,即为题目所求,然后就可以 O ( 1 ) O(1) O(1)输出询问了。
\qquad 预计至少有25分,但是它完美的只过了三个点
第二想法
\qquad 后面 f f f数组显然无法优化,所以只能考虑统计的优化,详见代码.
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int p=2000005;
int n,q;
ll f[5005][5005];//不开ll可能会炸
int h[5000005],a[5005];
inline int read(){
register int f=1,x=0;
register char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return f*x;
}
int main () {
n=read(),q=read();
for(register int i=1; i<=n; i++)a[i]=read();
h[a[1]+p]++;
h[a[2]+p]++;//第一个和第二个数预先加入
for(register int i=1; i<=n-2; i++) {
f[i][i]=f[i][i+1]=0;
h[a[i]+p]--;//删掉第i个数的数据
if(i&1)for(register int j=i+2; j<=n; j++) {
f[i][j]=h[p-a[i]-a[j]];
h[a[j]+p]++;//先计算完个数后,再把数字的统计加进去.
}//顺着用
else {
for(register int j=n; j>=i+2; j--) {
h[a[j]+p]--;
f[i][j]=h[p-a[i]-a[j]];//先减完再计算
}//逆着用
h[a[i+2]+p]++;//想一下为什么要+1,不加会WA.
}
}//通过来回利用来减少修改的次数,省去了清空的复杂度
for(register int i=1; i<=n; i++) {
for(register int j=i+1; j<=n; j++) {
f[i][j]+=f[i][j-1];
}
}
for(register int j=1; j<=n; j++) {
for(register int i=j-1; i; i--)f[i][j]+=f[i+1][j];
}
register int x,y;
for(int i=1; i<=q; i++) {
x=read(),y=read();
printf("%lld\n",f[x][y]);
}
return 0;
}