用线段树维护区间最大值;查询时返回区间最大,更新时在单点位置做 `max(原值, 新值)` 的只升不降修改。
OJ: luogu
题目 ID: P1531
难度:普及-
标签:线段树区间数据结构模板题
日期: 2026-06-21 01:44
题意
给出一列成绩,需要支持两种操作:
- 查询某段区间内的最高分
- 把某个位置的成绩改成
max(当前值, 给定值)
思路
先看一个可以直接验证想法的朴素解:
cpp
#include <bits/stdc++.h>
using namespace std;
// brute.cpp:直接维护数组。
// 查询时暴力扫区间,修改时做 max 更新。
const int MAXN = 200005;
int n, m;
int a[MAXN];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= m; i++) {
char op;
int x, y;
cin >> op >> x >> y;
if (op == 'Q') {
if (x > y) {
swap(x, y);
}
int ans = 0;
for (int j = x; j <= y; j++) {
ans = max(ans, a[j]);
}
cout << ans << '\n';
} else {
a[x] = max(a[x], y);
}
}
return 0;
}brute.cpp 用数组直接维护成绩。
查询时暴力扫区间最大值,更新时做一次 max 修改。
当数据量变大后,瓶颈显然出在区间查询。
这题是最标准的线段树区间最大值模型:
- 每个节点维护自己这段区间的最大成绩
于是:
- 查询时,把目标区间拆成若干线段树节点取最大
- 更新时,递归到单点位置,修改后一路回溯更新祖先
这题还有一个小细节:
- 更新不是赋值
- 而是
a[x] = max(a[x], y)
所以在线段树叶子处也要按这个语义处理。
代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
int n, m;
int a[MAXN];
int seg[MAXN << 2];
void push_up(int u) {
seg[u] = max(seg[u << 1], seg[u << 1 | 1]);
}
void build(int u, int l, int r) {
if (l == r) {
seg[u] = a[l];
return;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
push_up(u);
}
void update(int u, int l, int r, int pos, int val) {
if (l == r) {
// 题目要求的是把成绩改成 max(原成绩, 新成绩)。
seg[u] = max(seg[u], val);
return;
}
int mid = (l + r) >> 1;
if (pos <= mid) {
update(u << 1, l, mid, pos, val);
} else {
update(u << 1 | 1, mid + 1, r, pos, val);
}
push_up(u);
}
int query(int u, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
return seg[u];
}
int mid = (l + r) >> 1;
int ans = 0;
if (ql <= mid) {
ans = max(ans, query(u << 1, l, mid, ql, qr));
}
if (qr > mid) {
ans = max(ans, query(u << 1 | 1, mid + 1, r, ql, qr));
}
return ans;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
build(1, 1, n);
for (int i = 1; i <= m; i++) {
char op;
int x, y;
cin >> op >> x >> y;
if (op == 'Q') {
if (x > y) {
swap(x, y);
}
cout << query(1, 1, n, x, y) << '\n';
} else {
update(1, 1, n, x, y);
}
}
return 0;
}复杂度
- 建树:
- 单点更新:
- 区间查询:
空间复杂度:
总结
这题就是区间最大值线段树模板。
唯一需要特别留意的是更新操作不是直接覆盖,而是“只升不降”。