P6006 [USACO20JAN]Farmer John Solves 3SUM G:二维前缀和

题目来源:[USACO20JAN]Farmer John Solves 3SUM G
题目大意:找出任意区间三个数加起来和为0的个数。
一开始想N3N^3;后来用个桶想&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]);	
	}
	
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章