题目来源:[USACO20JAN]Farmer John Solves 3SUM G
题目大意:找出任意区间三个数加起来和为0的个数。
一开始想;后来用个桶想&N^2$,还是有问题。原来设i<j<k,或k<i<j都统计的很糊涂。
后来看题解才弄明白,设i<k<j,设sum[i][j]为i为起点,j为终点的情况下满足条件的个数,然后用二维前缀和维护,查询用二维差分o(1)完成。
参考代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5005,M=1E6+5;
int n,q,t[2*M];
ll a[N],sum[N][N];
int work0(int l,int r){
int cnt=0;
for(int i=l;i<=r-2;i++)
for(int j=i+1;j<=r-1;j++)
for(int k=j+1;k<=r;k++)
if(a[i]+a[j]+a[k]==0){
cnt++;
}
return cnt;
}
void work(){
for(int i=1;i<n;i++){//i<k<j,
for(int j=i+1;j<=n;j++){//应该统计i,到j中间有多少
ll tmp=-a[i]-a[j];
if(j>=i+2&&tmp+M<2*M&&tmp+M>=0){
sum[i][j]=t[tmp+M];//这是算的从i到j
// cout<<i<<" "<<j<<" "<<tmp<<" t"<<t[tmp+M]<<endl;
}
t[a[j]+M]++;
}
for(int j=i+1;j<=n;j++)t[a[j]+M]--;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
return ;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
work();
for(int i=1;i<=q;i++){
int l,r;
scanf("%d%d",&l,&r);
printf("%lld\n",sum[r][r]-sum[l-1][r]-sum[r][l-1]+sum[l-1][l-1]);
}
}