題目來源:[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]);
}
}