题解 洛谷 P4513 【小白逛公园】

题意简化:

1.给出 $m$ 个询问:区间最大子段和。

2.可支持在线修改。

------------
Solution:
有一题 p1115 题意类似,只不过不支持修改,可以很容易得到 dp 方程:

dp[i]=max(dp[i-1]+a[i],a[i]);

但本题须支持修改,考虑用线段树维护。

线段树本质上将该数列不断分成更小的区间,最后再合并信息。

考虑一段区间 l~r,将其分成两个区间:l~mid,mid+1~r,那么对于该区间,答案一定为左区间,右区间,或两区间合起来中的一个。

即:

题解 洛谷 P4513 【小白逛公园】

答案即为黄红紫三个区间中的一个,这也就说明了线段树需要保存的东西:区间最大:$maxn$,从左端点起向右的最大值:$lmax$,从右端点向左的最大值:$rmax$。

考虑如何维护 $lmax$,$rmax$:

例:

题解 洛谷 P4513 【小白逛公园】

$lmax$ 取 max(左子树 $lmax$,左子树与右子树 $lmax$ 之和)。

$rmax$ 同理,故我们需维护区间和。

上代码(加注释):

#include<bits/stdc++.h>
#define size 5001000
using namespace std;

struct SegmentTree
{
	int l,r,maxn,lmax,rmax,sum;
}t[size<<2];
int n,m,hxt[size];

inline void pushup(int p)
{	
	t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
	t[p].maxn=max(t[p<<1].maxn,max(t[p<<1|1].maxn,t[p<<1].rmax+t[p<<1|1].lmax));
	t[p].lmax=max(t[p<<1].lmax,t[p<<1].sum+t[p<<1|1].lmax);
	t[p].rmax=max(t[p<<1|1].rmax,t[p<<1|1].sum+t[p<<1].rmax);
}//上述维护lmax,rmax,maxn,sum的过程

inline void build(int l,int r,int p)
{
	t[p].l=l,t[p].r=r;
	if(l==r)
	{
		t[p].sum=hxt[l];
		t[p].lmax=hxt[l];
		t[p].rmax=hxt[l];
		t[p].maxn=hxt[l];
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,p<<1);
	build(mid+1,r,p<<1|1);
	pushup(p);
}//建树

inline void change(int pos,int d,int p)
{
	if(t[p].l==t[p].r)
	{
		t[p].lmax=d;
		t[p].rmax=d;
		t[p].maxn=d;
		t[p].sum=d;
		return;
	}
	int mid=(t[p].l+t[p].r)>>1;
	if(pos<=mid) change(pos,d,p<<1);
	if(pos>mid) change(pos,d,p<<1|1);
	pushup(p);
}//替换

inline SegmentTree ask(int l,int r,int p)
{
	if(l<=t[p].l&&r>=t[p].r) return t[p];
	int mid=(t[p].l+t[p].r)>>1;
	SegmentTree val;
	if(r<=mid) val=ask(l,r,p<<1);
	if(l>mid) val=ask(l,r,p<<1|1);
	if(l<=mid&&r>mid)
	{
		SegmentTree a,b;
		a=ask(l,r,p<<1);
		b=ask(l,r,p<<1|1);
		val.lmax=max(a.lmax,a.sum+b.lmax);
		val.rmax=max(b.rmax,b.sum+a.rmax);
		val.maxn=max(b.maxn,max(a.maxn,a.rmax+b.lmax));
	}//查询时,可能有三种情况:左,右,组合,故需返回多个值,此处返回结构体,同时维护查询的值
	return val;
}

int main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&hxt[i]);
	build(1,n,1);
	for(int i=1;i<=m;i++)
	{
		int k,x,y;
		scanf("%d %d %d",&k,&x,&y);
		if(k==1)
		{
			if(x>y) swap(x,y);//小细节
			printf("%d\n",ask(x,y,1).maxn);
		}
		else 
		{
			change(x,y,1);
		}
	}
	return 0;
}

于是,此题就做出来了。

return 0;

上一篇:最大流最小割入门题


下一篇:题解 SP1716 【GSS3 - Can you answer these queries III】