P4513 小白逛公园 习题笔记(线段树维护区间最大连续子段和)

文章讲述了如何使用线段树数据结构解决一道名为P4513的问题,涉及到区间和、左右区间最大和以及连续子段和的维护,特别提到了区间修改时的更新策略和查询操作的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P4513本文参考了董晓老师的博客

这道题着实想了很长时间(新手),只能想到一个O(mn)的dp普通写法,那么遇上区间修改问题改怎么操作呢。答案很明显,线段树!

这道题的线段树主要维护四个信息

区间和,最大左子段和,最大右子段和,与区间最大连续子段和(是不是感觉很眼熟呢),在我的印象中,吉司机线段树(用min直接维护区间)与这题思路相类似。

这里的query还用了一种新的方式来维护

下面直接贴代码(见注释)

// Problem: 
//     P4513 小白逛公园
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P4513
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<iostream>
using namespace std;
const int N=5e5+10;
#define lc u<<1
#define rc u<<1|1
int a[N];
struct Tree{
	int l,r,sum,lmax,rmax,maxn;//区间和 左区间最大和 右区间最大 最大和
}tr[N*4];

void pushup(Tree &t,Tree a,Tree b){//这里和下面的query是最关键的两个地方
	t.sum=a.sum+b.sum;//求和
	t.lmax=max(a.lmax,b.lmax+a.sum);//左侧最大来自于左区间左侧和左区间+右区间左侧最大的
	t.rmax=max(b.rmax,a.rmax+b.sum);//与上面思路相同
	t.maxn=max(max(a.maxn,b.maxn),a.rmax+b.lmax);//来自于下面两边最大的,和中间的(前提连续)
}

Tree query(int u,int l,int r){
	if(l<=tr[u].l&&tr[u].r<=r){
		return tr[u];//直接返回这个类型
	}
	int m=(tr[u].l+tr[u].r)>>1;
	if(r<=m) return query(lc,l,r);
	if(l>m) return query(rc,l,r);
//这种操作与平常相反,原因是我们的线段树只能维护一定的线段,如果遇到断点就要另外处理,于是这里的两行意思是,全部在某一边,没有断点,这样一来就可以直接返回
	Tree t;
	pushup(t,query(lc,l,m),query(rc,m+1,r));
//这里是有断点的,就以pushup的方式合并一个(本来没有,所以直接开一个新的t)
	return t;
}
//需要注意 上面判断是l-r,因为直接包含了,接着找区间就好,下面的是断点,所以直接分开处理

void update(int u,int x,int k){
	if(tr[u].l==x&&tr[u].r==x){
		tr[u]={x,x,k,k,k,k};
		return;
	}
    int m=(tr[u].l+tr[u].r)>>1;
    if(x<=m) update(lc,x,k);
    if(x>m) update(rc,x,k);
    pushup(tr[u],tr[lc],tr[rc]);
}

void build(int u,int l,int r){
	tr[u]={l,r};//这里不能忘了,因为这里我荣幸WA一发
	if(l==r){
		tr[u]={l,r,a[l],a[l],a[l],a[l]};
		return;
	}
	int m=(l+r)>>1;
	build(lc,l,m);
	build(rc,m+1,r);
	pushup(tr[u],tr[lc],tr[rc]);
}

int main(){
	int n,m;cin>>n>>m;
	for(int i=1;i<=n;++i) cin>>a[i];
	build(1,1,n);
	while(m--){
		int op,b,c;cin>>op>>b>>c;
		if(op==1){
			if(b>c) swap(b,c);//交换,swap是c++的自带
			cout<<query(1,b,c).maxn<<endl;
		}
		else{
			update(1,b,c);//第b个变成了c
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值