P4513 小白逛公园 线段树

254 阅读3分钟

链接

小白逛公园
题目大意:对区间进行单点修改和子区间最大权值的查询

思路

可以用线段树来维护子区间的最大权值。
当查询区间p的子区间最大权值时,设其左区间为lc,右区间为rc,中间点为mid
p的子区间最大权值可能来自以下三种情况:

  1. 完全来自lc
  2. 完全来自rc
  3. 从左区间取一段,从右区间取一段,且该区间跨过mid

对于情况1和情况2

p的答案即对应左区间和右区间的答案。

对于情况3

lst为区间lc的最大后缀和,pre为区间rc的最大前缀和,那么这段区间就是lst+pre
如下图所示:

子区间的最大权值
当维护p的答案的时候,只需要在这3个情况中取max即可。

如何维护区间plstpre(下面以pre的维护为例,自己思考lst的维护)

  1. p为叶结点时,很显然plst=叶结点的值;
  2. 否则,ppre可以完全是左区间的pre,也可以是左区间的总和加上右区间的pre。 可得:p.pre=max(lc.pre,lc.sum+rc.pre)
    综上,一个线段树的结点需要保存的内容有:
    l,r,pre,lst,sum,bst
    其中lr分别为左右端点,bst为维护的答案。

其他

更新递归后结点时千万不要对当前结点取max
因为对子区间进行修改之后有可能为负值。

代码

#include<bits/stdc++.h>
#define l(p) t[p].l
#define r(p) t[p].r
#define sum(p) t[p].sum
#define bst(p) t[p].bst
#define pre(p) t[p].pre
#define lst(p) t[p].lst
#define lc p<<1
#define rc p<<1|1
using namespace std;
const int N=500000+10;
struct Node{
	int l,r,sum,bst,pre,lst;
}t[4*N];
int n,m,k,x,y;
int a[N];
inline int max_(int q,int w,int e)
{
	q=(q>w)?q:w;
	q=(q>e)?q:e;
	return q;
}
inline int read()
{
	int ret=0,flag=1;char ch=getchar();
	while(ch<'0' || ch>'9')
	{
		if(ch=='-')flag=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9')ret=ret*10+ch-'0',ch=getchar();
	return ret*flag;
}
inline void update(int p)		//更新结点p 
{
	sum(p)=sum(lc)+sum(rc);
	pre(p)=max(pre(lc),sum(lc)+pre(rc));
	lst(p)=max(lst(rc),sum(rc)+lst(lc));
	bst(p)=max_(bst(lc),bst(rc),lst(lc)+pre(rc));
}
inline void sete(int p,int val)	//为叶结点赋值 
{
	sum(p)=bst(p)=pre(p)=lst(p)=val;
}
void build(int p)
{
	if(l(p)==r(p))
	{
		sete(p,a[l(p)]);
		return;
	}
	int mid=(l(p)+r(p))>>1;
	l(lc)=l(p);r(lc)=mid;
	l(rc)=mid+1;r(rc)=r(p);
	build(lc);
	build(rc);
	update(p);
}
void modify(int p,int aim,int val)	//修改操作 
{
	if(l(p)==aim && r(p)==aim)		//如果到达叶结点且是目标位置 
	{
		sete(p,val);
		return;
	}
	int mid=(l(p)+r(p))>>1;
	if(aim<=mid)modify(lc,aim,val);
	else modify(rc,aim,val);
	update(p);
}
Node query(int p,int al,int ar)	//查询操作 
{
	if(al<=l(p) && ar>=r(p))
	return t[p];
	Node ret=(Node){0,0,0,0,0,0};
	int mid=(l(p)+r(p))>>1;
	if(ar<=mid)return query(lc,al,ar);
	else if(al>mid)return query(rc,al,ar);
	else
	{
		Node lft=query(lc,al,ar);
		Node rht=query(rc,al,ar);
		ret.bst=max_(lft.bst,rht.bst,lft.lst+rht.pre);
		ret.pre=max(lft.pre,lft.sum+rht.pre);
		ret.lst=max(rht.lst,rht.sum+lft.lst);
		ret.sum=lft.sum+rht.sum;
		return ret;
	}
}
int main()
{
	n=read();m=read();
	for(register int i=1;i<=n;++i)
	a[i]=read();
	l(1)=1;r(1)=n;
	build(1);
	while(m--)
	{
		k=read();x=read();y=read();
		if(x>y && k==1)swap(x,y);
		if(k==1)
		{
			printf("%d\n",query(1,x,y).bst);
		}
		else modify(1,x,y);
	}
}