luogu P4513 小白逛公园(维护区间最大子段和)

题目链接:P4513 小白逛公园 

题意简述

小白对于他逛的公园,都有一个评价值。现在,他一共有两个操作需要你进行维护。

1、询问在一个区间中,最大的子区间(求区间最大子段和)

2、改变对某个公园的评价(单点修改)

input

// n个公园,m个操作
5 3
//公园的初始评价
1
2
-3
4
5
// 操作
1 2 3
2 2 -1
1 2 3

 思路

构建线段树

要维护动态的区间最大子段和,则需要构建一颗可以进行查询最大子段和线段树。

1、维护区间总和sum

2、维护区间左端最大子段和lmax

3、维护区间右端最大子段和rmax

4、维护区间最大子段和mmax

其次,子节点的信息应该如何传给他的父亲节点呢?

1、对于区间总和sum,直接进行区间加即可,tr[fa].sum=tr[lc].sum+tr[rc].sum; 

2、对于左端的最大子段和,可以是左子树的左端最大子段和,也可以是一整个左子树和右子树的左端子段和拼接起来

则有:tr[fa].lmax=max(tr[lc].lmax,tr[lc].sum+tr[rc].lmax);

3、对于右端的最大子段和,可以是右子树的右端最大子段和,也可以是一整个右子树和左子树的右端最大子段和拼接起来

则有:tr[fa].rmax=max(tr[rc.rmax,tr[rc].sum+tr[lc].rmax);

4、对于区间最大子段和,则有三种来源,左子树的最大子段和,右子树的最大子段和,左子树的右端最大子段和和右子树的左端最大子段和拼接起来

则有:tr[fa].mmax=max(tr[lc].mmax,tr[rc].mmax,tr[lc].rmax+tr[rc].lmax);

修改与查询

修改

修改是单点修改,不是很复杂,一直遍历到这个公园所在的叶子节点,然后直接修改,再向上pushup传递子树信息即可。

查询

对于查询操作,则有一些难度。对于一颗线段树,许多节点都是有两个子节点的,那么该如何进行传递信息呢?这也是这道题十分值得讲的一个点。

其实,对于我们的函数,不仅仅是void,int,long long,还可以自己定义返回值,比如这道题,我们的线段树的结构体为 Tree ,那么我们就可以将查询操作的返回值定义为 Tree ,那么int类型只能返回一个值的束缚就没有了。具体操作,可以看Code。

我们就可以建立一个临时节点,记录左右两种遍历所得到的信息,然后向上传即可。

Code(不足可指出)

#include<bits/stdc++.h>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>

using namespace std;

inline int read() {
	int x=0,f=1;char s=getchar();
	while (s>'9'||s<'0') {
		if (s=='-') f=-f;
		s=getchar();
	}
	while (s>='0'&&s<='9') {
		x=(x<<1)+(x<<3)+(s^48);
		s=getchar();
	}
	return x*f;
}

const int N = 5e5+10;

int n,m,a[N],op,x,y;
struct Tree{
	int sum,lmax,rmax,mmax;
}tr[N<<2];

void pushup(Tree &T,Tree lc,Tree rc) { // pushup 向上传递信息 
	T.sum=lc.sum+rc.sum;  
	T.lmax=max(lc.lmax,lc.sum+rc.lmax);
	T.rmax=max(rc.rmax,rc.sum+lc.rmax);
	T.mmax=max(lc.mmax,max(rc.mmax,lc.rmax+rc.lmax));
	return;
}

void build(int k,int l,int r) {  //建立线段树 
	if (l==r) {
		tr[k]={a[l],a[l],a[l],a[l]};
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(tr[k],tr[k<<1],tr[k<<1|1]);
}

void modify(int k,int l,int r,int xx,int v) {
	if (l==r) { //已经到达了这个叶子节点了,修改完成之后直接返回 
		tr[k]={v,v,v,v};
		return;
	}
	int mid=(l+r)>>1;
	if (mid>=xx) modify(k<<1,l,mid,xx,v);  
	if (mid<xx) modify(k<<1|1,mid+1,r,xx,v);
	pushup(tr[k],tr[k<<1],tr[k<<1|1]);
	return;
}

Tree ser(int k,int l,int r,int zl,int zr) {
	if (l>=zl&&r<=zr) return tr[k];   //当前这个节点被查询区间包含,直接返回即可 
	int mid=(l+r)>>1;
	if (mid>=zr) return ser(k<<1,l,mid,zl,zr);  //当mid<zr 时要遍历右子树,此时这种情况只会遍历左子树 
	if (mid<zl) return ser(k<<1|1,mid+1,r,zl,zr); // 同理 
	Tree res,lc,rc;  
	//res 是建立的临时的记录查询结果的节点
	//lc 是查询左子树的结果
	//rc 是查询右子树的结果 
	lc=ser(k<<1,l,mid,zl,zr);
	rc=ser(k<<1|1,mid+1,r,zl,zr);
	pushup(res,lc,rc);//将左右节点的信息整合,向上传递 
	return res;
}

int main(){
	n=read();m=read();
	for (int i=1;i<=n;++i) a[i]=read();
	build(1,1,n);
	for (int i=1;i<=m;++i) {
		op=read();x=read();y=read();
		if (op==1) {
			if (x>y) swap(x,y);
			Tree ans=ser(1,1,n,x,y);
			printf("%d\n",ans.mmax); 
		}
		else {
			modify(1,1,n,x,y);
		}
	}
	return 0;
}

到此,就结束了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值