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) (1≤a,b≤n)。测试数据可能会出现 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(1≤p≤N)。
输出格式
小白每出去玩一次,都对应输出一行,只包含一个整数,表示小白可以选出的公园得分和的最大值。
样例 #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 1≤n≤5×105, 1 ≤ m ≤ 1 0 5 1 \le m \le 10^5 1≤m≤105,所有打分都是绝对值不超过 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;
}