把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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\n",ans[i]);
	return 0;
}

posted @ 2020-11-15 15:43  Starlight_Glimmer  阅读(56)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end