P4513 小白逛公园(线段树 + 连续区间和最值)

P4513 小白逛公园(线段树 + 连续区间和最值)

https://www.luogu.com.cn/problem/P4513

感觉这还是一道不错的经典线段树题。此题用来练习线段树,维护并求连续区间最值问题

————————————————————————————————————————————————————————————

小白逛公园

题目背景

小新经常陪小白去公园玩,也就是所谓的遛狗啦…

题目描述

在小新家附近有一条“公园路”,路的一边从南到北依次排着 n n n 个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了。

一开始,小白就根据公园的风景给每个公园打了分。小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第 a a a 个和第 b b b 个公园之间(包括 a , b a, b a,b 两个公园)选择连续的一些公园玩。小白当然希望选出的公园的分数总和尽量高咯。同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化。

那么,就请你来帮小白选择公园吧。

输入格式

第一行,两个整数 n n n m m m,分别表示表示公园的数量和操作(遛狗或者改变打分)总数。

接下来 n n n 行,每行一个整数,依次给出小白开始时对公园的打分。

接下来 m m m 行,每行三个整数。其中第一个整数 k k k 1 1 1 2 2 2

  • k = 1 k=1 k=1 表示,小新要带小白出去玩,接下来的两个整数 a a a b b b 给出了选择公园的范围 ( 1 ≤ a , b ≤ n ) (1 \le a,b \le n) (1a,bn)。测试数据可能会出现 a > b a > b a>b 的情况,需要进行交换;
  • k = 2 k=2 k=2 表示,小白改变了对某个公园的打分,接下来的两个整数 p p p s s s,表示小白对第 p p p 个公园的打分变成了 s ( 1 ≤ p ≤ N ) s(1\le p\le N) s(1pN)

输出格式

小白每出去玩一次,都对应输出一行,只包含一个整数,表示小白可以选出的公园得分和的最大值。

样例 #1

样例输入 #1

5 3
1
2
-3
4
5
1 2 3
2 2 -1
1 2 3

样例输出 #1

2
-1

提示

数据规模与约定

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 5 × 1 0 5 1 \le n \le 5 \times 10^5 1n5×105 1 ≤ m ≤ 1 0 5 1 \le m \le 10^5 1m105,所有打分都是绝对值不超过 1000 1000 1000 的整数。

————————————————————————————————————————————————————————————

基本思路
连续区间的和的最值,如果要用线段树来维护的话,重点就在于pushup中的操作。

想象以下,两个连续的小区间合并为一个大区间,我们该怎么维护最大的连续区间和?我们可以想到,这个大区间的连续区间的位置有几种可能:

  • 全部位于左侧的小区间
  • 全部位于右侧的小区间
  • 一部分在左侧小区间(靠右),一部分在右侧小区间(靠左)

那么对于每个区间的最大连续区间和,我们只需要在合并的时候将这三种情况进行比较即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int M = 5e5 + 10;
const int inf = 1e9 + 5;

ll n, m;
ll a[M];

struct node
{
	ll sum;//区间的和
	ll maxl;//区间从左边界开始向右连续,能得到的最大的区间和
	ll maxr;//区间从有边界开始向左连续,能得到的最大的区间和
	ll ans;//这个区间中的最大连续区间和
}t[M << 2];

void pushup(int o)
{
	//区间sum合并
	t[o].sum = t[o << 1].sum + t[o << 1 | 1].sum;
	//用max(只来自左区间,左区间所有+右区间左侧部分),比较处大区间的左边界开始连续最大区间和
	t[o].maxl = max(t[o << 1].maxl, t[o << 1].sum + t[o << 1 | 1].maxl);
	//同理,用max(只来自右区间,右区间所有+左区间右侧部分),比较处大区间的右边界开始连续最大区间和
	t[o].maxr = max(t[o << 1 | 1].maxr, t[o << 1 | 1].sum + t[o << 1].maxr);
	//比较上面所述三种情况中的最大值,作为这个区间的连续区间最大值答案
	t[o].ans = max(t[o << 1].maxr + t[o << 1 | 1].maxl, max(t[o << 1].ans, t[o << 1 | 1].ans));
}

void buildtree(int s, int e, int o)
{
	if (s == e)
	{
		t[o].ans = t[o].maxl = t[o].maxr = t[o].sum = a[s];
		return;
	}

	int mid = (s + e) >> 1;

	buildtree(s, mid, o << 1);
	buildtree(mid + 1, e, o << 1 | 1);

	pushup(o);
}


void update(int s, int e, int o, int x, ll p)
{
	if (s == e)
	{
		t[o].ans = t[o].maxl = t[o].maxr = t[o].sum = p;
		return;
	}
	int mid = (s + e) >> 1;

	if (x <= mid)
		update(s, mid, o << 1, x, p);
	else
		update(mid + 1, e, o << 1 | 1, x, p);

	pushup(o);
}

node query(int s, int e, int o, int l, int r)//返回的是一个结构体,方便我们查找
{
	if (l <= s && e <= r)
		return t[o];

	int mid = (s + e) >> 1;

	if (l > mid)//如果探查区间完全在右半边
		return query(mid + 1, e, o << 1 | 1, l, r);
	else if (r < mid + 1)//如果探查区间完全在左半边
		return query(s, mid, o << 1, l, r);
	else//所探查的区间部分在左,部分在右
	{
		node templ, tempr, ans;//返回结构体,为的就是这里方便
		//思路跟pushup差不多
		templ = query(s, mid, o << 1, l, r);
		tempr = query(mid + 1, e, o << 1 | 1, l, r);

		ans.maxl = max(templ.maxl, templ.sum + tempr.maxl);
		ans.maxr = max(tempr.maxr, templ.maxr + tempr.sum);
		ans.ans = max(max(templ.ans, tempr.ans), templ.maxr + tempr.maxl);

		return ans;
	}
}


void solve()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	buildtree(1, n, 1);

	for (int i = 1; i <= m; i++)
	{
		ll opt;
		cin >> opt;

		if (opt == 1)
		{
			ll a, b;
			cin >> a >> b;

			if (a > b)
				swap(a, b);

			cout << query(1, n, 1, a, b).ans << '\n';

		}
		else
		{
			ll p, s;
			cin >> p >> s;
			update(1, n, 1, p, s);
		}
	}
}

int main()
{
	std::ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t = 1;
	//cin >> t;
	while (t--)
		solve();
	return 0;
}
  • 11
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值