SPOJ1557 GSS2-Can you answer these queries II【线段树】

题目链接

题目解析

呐,是一道非常有意思的线段树的题呢(奇怪的语气

仅仅只是比GSS1多了一个去重的要求,思维方式就完全不一样了呢(更奇怪的语气

咳咳

像GSS1那样简单粗暴地维护(sum,mx,mxl,mxr)是无法处理“重复”这个限制的,我们需要用一种不同的方式来处理,我也是看了题解才知道怎么做,不过方法真的很妙!

我们把询问离线下来,按照右端点从小到大排。

然后遍历(1)(n),依次插入(a[i]),然后对每个(r==i)的询问进行处理。这么做的话,就达到了查询时,没有右端点右边的元素的效果。

首先,我们引入(A[j]=a[j]+a[j+1]+...+a[i])

即:

(A[1]=a[1]+a[2]+a[3]+...+a[i])

(A[2]=a[2]+a[3]+...+a[i])

(A[3]=a[3]+...+a[i])

...

(A[i]=a[i])

(A[i+1]=0)

关于插入

考虑插入一个新元素(a[i+1]),这时(i=i+1)

所以(A[j]=a[j]+a[j+1]+...+a[i]+a[i+1])

讲道理,我们应该把每一个(A[j])加上(a[i+1]),但是要去重,所以我们应该把不含(a[i+1])的所有(A[j])加上(a[i+1])

那么具体是哪些数呢?设(pre[a[i+1]])是上一次(a[i+1])出现的位置,那么在更新(pre[a[i+1]])那个位置的时候,我们就已经把所有的(pre[a[i+1]])以前的(A[j])都已经加了一次(a[i+1])了,它们不需要重复再加,所以应该把区间([pre[a[i+1]]+1,i+1])之间的数集体加(a[i+1])

小结:插入(a[i+1])时,把(A[pre[a[i+1]]+1])(A[i+1])之间的所有数加(a[i+1])

容易发现这是一个区间加法,我们可以对(A[])建线段树维护。

关于查询

对于一个查询区间([l,r],r==i),我们在插入(a[i])之后进行查询。

如果枚举一个答案的左端点(j,j>=l),发现由于(A[])中不含(r)右边的元素,所以任意一个(A[j])的右端点都在(r)以内,都是合法的,那么我们以(j)为区间左端点的产生的答案就是(A[j])出现过的历史最大值。我们要找([l,r])的答案,将枚举(j)产生的所有答案取最大值即可。

小结:查询([l,r],r==i),在插入(a[i])之后进行查询,枚举(j>=l),答案是所有(A[j])的历史最大值的最大值。

那么在线段树上再维护一个历史最大和即可。


►Code View

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
#define LL long long
#define N 100005
#define DEL 100000
#define INF 0x3f3f3f3f
#define MOD 998244353
int rd()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48); c=getchar();}
	return f*x;
}
int n,m,a[N],pre[N<<1],ans[N];
struct node{
	int sum,hmx,stag,htag;
}tree[N<<2];
/*
叶节点 
sum:对应的A[j]的值 即a[j]+a[j+1]+...+a[i]
hmx:sum的历史最大值
其它节点
sum:对应区间sum的最大值
hmx:对应区间hmx的最大值
stag和htag分别为对应的懒标记 
stag是当前区间应该加多少
htag是从上一次下传标记到现在出现过的历史最大懒标记(即出现过的最大的往下面区间加多少的值 
*/
struct queries{
	int l,r,id;
}q[N];
bool cmp(queries x,queries y)
{
	return x.r<y.r;
}
void PushUp(int i)
{
	tree[i].sum=max(tree[i<<1].sum,tree[i<<1|1].sum);
	tree[i].hmx=max(tree[i<<1].hmx,tree[i<<1|1].hmx);
	tree[i].stag=tree[i].htag=0;
}
void PushDown(int i)
{
	tree[i<<1].hmx=max(tree[i<<1].hmx,tree[i<<1].sum+tree[i].htag);
	tree[i<<1].sum+=tree[i].stag;
	tree[i<<1].htag=max(tree[i<<1].htag,tree[i<<1].stag+tree[i].htag);
	tree[i<<1].stag+=tree[i].stag;
	
	tree[i<<1|1].hmx=max(tree[i<<1|1].hmx,tree[i<<1|1].sum+tree[i].htag);
	tree[i<<1|1].sum+=tree[i].stag;
	tree[i<<1|1].htag=max(tree[i<<1|1].htag,tree[i<<1|1].stag+tree[i].htag);
	tree[i<<1|1].stag+=tree[i].stag;
	
	tree[i].stag=tree[i].htag=0;
}
void Update(int i,int l,int r,int ql,int qr,int val)
{
	if(ql<=l&&r<=qr)
	{
		tree[i].sum+=val,tree[i].hmx=max(tree[i].hmx,tree[i].sum);
		tree[i].stag+=val,tree[i].htag=max(tree[i].htag,tree[i].stag);
		return ;
	}
	PushDown(i);
	int mid=(l+r)>>1;
	if(ql<=mid) Update(i<<1,l,mid,ql,qr,val);
	if(qr>mid) Update(i<<1|1,mid+1,r,ql,qr,val);
	PushUp(i);
	return ;
}
node Query(int i,int l,int r,int ql,int qr)
{
	if(ql<=l&&r<=qr)
		return tree[i];
	PushDown(i);
	int mid=(l+r)>>1;
	if(qr<=mid) return Query(i<<1,l,mid,ql,qr);
	else if(ql>mid) return Query(i<<1|1,mid+1,r,ql,qr);
	else
	{
		node x=Query(i<<1,l,mid,ql,qr),y=Query(i<<1|1,mid+1,r,ql,qr),res;
		res.sum=max(x.sum,y.sum);
		res.hmx=max(x.hmx,y.hmx);
		res.htag=res.stag=0;
		return res;
	}
}
int main()
{
	n=rd();
	for(int i=1;i<=n;i++)
		a[i]=rd();
	m=rd();
	for(int i=1;i<=m;i++)
		q[i].l=rd(),q[i].r=rd(),q[i].id=i;
	sort(q+1,q+m+1,cmp);
	int j=1;
	for(int i=1;i<=n;i++)
	{
		Update(1,1,n,pre[a[i]+DEL]+1,i,a[i]);
		pre[a[i]+DEL]=i;
		while(q[j].r==i&&j<=m)
			ans[q[j].id]=Query(1,1,n,q[j].l,q[j].r).hmx,j++;
	}
	for(int i=1;i<=m;i++)
		printf("%d
",ans[i]);
	return 0;
}

原文地址:https://www.cnblogs.com/lyttt/p/13976957.html