区间最值操作 & 区间历史最值
本文讲解吉老师在 2016 年国家集训队论文中提到的线段树处理历史区间最值的问题。
区间最值
笼统地说,区间最值操作指,将区间
区间取
- 如果
,显然这个 是没有意义的,直接返回; - 如果
,那么这个 就能更新当前区间中的最大值。于是我们让区间和加上 ,然后更新 为 ,并打一个标记。 - 如果
,那么这时你发现你不知道有多少个数涉及到更新的问题。于是我们的策略就是,暴力递归向下操作。然后上传信息。
这个算法的复杂度如何?使用势能分析法可以得到复杂度是
#include <algorithm>
#include <cctype>
#include <iostream>
using namespace std;
constexpr int N = 1e6 + 6;
int t, n, m;
int a[N];
int mx[N << 2], se[N << 2], cn[N << 2], tag[N << 2];
long long sum[N << 2];
void pushup(int u) { // 向上更新标记
const int ls = u << 1, rs = u << 1 | 1;
sum[u] = sum[ls] + sum[rs];
if (mx[ls] == mx[rs]) {
mx[u] = mx[rs];
se[u] = max(se[ls], se[rs]);
cn[u] = cn[ls] + cn[rs];
} else if (mx[ls] > mx[rs]) {
mx[u] = mx[ls];
se[u] = max(se[ls], mx[rs]);
cn[u] = cn[ls];
} else {
mx[u] = mx[rs];
se[u] = max(mx[ls], se[rs]);
cn[u] = cn[rs];
}
}
void pushtag(int u, int tg) { // 单纯地打标记,不暴搜
if (mx[u] <= tg) return;
sum[u] += (1ll * tg - mx[u]) * cn[u];
mx[u] = tag[u] = tg;
}
void pushdown(int u) { // 下传标记
if (tag[u] == -1) return;
pushtag(u << 1, tag[u]), pushtag(u << 1 | 1, tag[u]);
tag[u] = -1;
}
void build(int u = 1, int l = 1, int r = n) { // 建树
tag[u] = -1;
if (l == r) {
sum[u] = mx[u] = a[l], se[u] = -1, cn[u] = 1;
return;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify_min(int L, int R, int v, int u = 1, int l = 1, int r = n) {
if (mx[u] <= v) return;
if (L <= l && r <= R && se[u] < v) return pushtag(u, v);
int mid = (l + r) >> 1;
pushdown(u);
if (L <= mid) modify_min(L, R, v, u << 1, l, mid);
if (mid < R) modify_min(L, R, v, u << 1 | 1, mid + 1, r);
pushup(u);
}
int query_max(int L, int R, int u = 1, int l = 1, int r = n) { // 查询最值
if (L <= l && r <= R) return mx[u];
int mid = (l + r) >> 1, r1 = -1, r2 = -1;
pushdown(u);
if (L <= mid) r1 = query_max(L, R, u << 1, l, mid);
if (mid < R) r2 = query_max(L, R, u << 1 | 1, mid + 1, r);
return max(r1, r2);
}
long long query_sum(int L, int R, int u = 1, int l = 1, int r = n) { // 数值
if (L <= l && r <= R) return sum[u];
int mid = (l + r) >> 1;
long long res = 0;
pushdown(u);
if (L <= mid) res += query_sum(L, R, u << 1, l, mid);
if (mid < R) res += query_sum(L, R, u << 1 | 1, mid + 1, r);
return res;
}
void go() { // 根据题意
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
build();
for (int i = 1; i <= m; i++) {
int op, x, y, z;
cin >> op >> x >> y;
if (op == 0)
cin >> z, modify_min(x, y, z);
else if (op == 1)
cout << query_max(x, y) << '\n';
else
cout << query_sum(x, y) << '\n';
}
}
signed main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> t;
while (t--) go();
return 0;
}
BZOJ4695 最假女选手
维护一个序列
1 l r x
。 2 l r x
。 3 l r x
。 4 l r
输出。 5 l r
输出。 6 l r
输出。
同样的方法,我们维护最大、次大、最大个数、最小、次小、最小个数、区间和。除了这些信息,我们还需要维护区间
- 我们认为区间加的标记是最优先的,其余两种标记地位平等。
- 对一个结点加上一个
标记,除了用 更新卫星信息和当前结点的区间加标记外,我们用这个 v 更新区间 和区间 的标记。 - 对一个结点取
的 (这里忽略暴搜的过程,假定标记满足添加的条件),除了更新卫星信息,我们要与区间 的标记做比较。如果 小于区间 的标记,则所有的数最后都会变成 v,那么把区间 的标记也变成 。否则不管。 - 区间取 v 的
同理。
在维护信息的时侯,当只有一个数或两个数的时侯可能发生数集重合,比如一个数既是最大值又是次小值,需要特判。
#include <iostream>
using namespace std;
constexpr int N = 5e5 + 5, SZ = N << 2, INF = 0x7fffffff;
int n, m;
int a[N];
struct data {
int mx, mx2, mn, mn2, cmx, cmn, tmx, tmn, tad;
long long sum;
} t[SZ];
void pushup(int u) {
const int lu = u << 1, ru = u << 1 | 1;
t[u].sum = t[lu].sum + t[ru].sum;
if (t[lu].mx == t[ru].mx) {
t[u].mx = t[lu].mx, t[u].cmx = t[lu].cmx + t[ru].cmx;
t[u].mx2 = max(t[lu].mx2, t[ru].mx2);
} else if (t[lu].mx > t[ru].mx) {
t[u].mx = t[lu].mx, t[u].cmx = t[lu].cmx;
t[u].mx2 = max(t[lu].mx2, t[ru].mx);
} else {
t[u].mx = t[ru].mx, t[u].cmx = t[ru].cmx;
t[u].mx2 = max(t[lu].mx, t[ru].mx2);
}
if (t[lu].mn == t[ru].mn) {
t[u].mn = t[lu].mn, t[u].cmn = t[lu].cmn + t[ru].cmn;
t[u].mn2 = min(t[lu].mn2, t[ru].mn2);
} else if (t[lu].mn < t[ru].mn) {
t[u].mn = t[lu].mn, t[u].cmn = t[lu].cmn;
t[u].mn2 = min(t[lu].mn2, t[ru].mn);
} else {
t[u].mn = t[ru].mn, t[u].cmn = t[ru].cmn;
t[u].mn2 = min(t[lu].mn, t[ru].mn2);
}
}
void push_add(int u, int l, int r, int v) {
// 更新加法标记的同时,更新 $\min$ 和 $\max$ 标记
t[u].sum += (r - l + 1ll) * v;
t[u].mx += v, t[u].mn += v;
if (t[u].mx2 != -INF) t[u].mx2 += v;
if (t[u].mn2 != INF) t[u].mn2 += v;
if (t[u].tmx != -INF) t[u].tmx += v;
if (t[u].tmn != INF) t[u].tmn += v;
t[u].tad += v;
}
void push_min(int u, int tg) {
// 注意比较 $\max$ 标记
if (t[u].mx <= tg) return;
t[u].sum += (tg * 1ll - t[u].mx) * t[u].cmx;
if (t[u].mn2 == t[u].mx) t[u].mn2 = tg; // !!!
if (t[u].mn == t[u].mx) t[u].mn = tg; // !!!!!
if (t[u].tmx > tg) t[u].tmx = tg; // 更新取 $\max$ 标记
t[u].mx = tg, t[u].tmn = tg;
}
void push_max(int u, int tg) {
if (t[u].mn > tg) return;
t[u].sum += (tg * 1ll - t[u].mn) * t[u].cmn;
if (t[u].mx2 == t[u].mn) t[u].mx2 = tg;
if (t[u].mx == t[u].mn) t[u].mx = tg;
if (t[u].tmn < tg) t[u].tmn = tg;
t[u].mn = tg, t[u].tmx = tg;
}
void pushdown(int u, int l, int r) {
const int lu = u << 1, ru = u << 1 | 1, mid = (l + r) >> 1;
if (t[u].tad)
push_add(lu, l, mid, t[u].tad), push_add(ru, mid + 1, r, t[u].tad);
if (t[u].tmx != -INF) push_max(lu, t[u].tmx), push_max(ru, t[u].tmx);
if (t[u].tmn != INF) push_min(lu, t[u].tmn), push_min(ru, t[u].tmn);
t[u].tad = 0, t[u].tmx = -INF, t[u].tmn = INF;
}
void build(int u = 1, int l = 1, int r = n) {
t[u].tmn = INF, t[u].tmx = -INF; // 取极限
if (l == r) {
t[u].sum = t[u].mx = t[u].mn = a[l];
t[u].mx2 = -INF, t[u].mn2 = INF;
t[u].cmx = t[u].cmn = 1;
return;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void add(int L, int R, int v, int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return;
if (L <= l && r <= R) return push_add(u, l, r, v); // !!! 忘 return
int mid = (l + r) >> 1;
pushdown(u, l, r);
add(L, R, v, u << 1, l, mid), add(L, R, v, u << 1 | 1, mid + 1, r);
pushup(u);
}
void tomin(int L, int R, int v, int u = 1, int l = 1, int r = n) {
if (R < l || r < L || t[u].mx <= v) return;
if (L <= l && r <= R && t[u].mx2 < v) return push_min(u, v); // BUG: 忘了返回
int mid = (l + r) >> 1;
pushdown(u, l, r);
tomin(L, R, v, u << 1, l, mid), tomin(L, R, v, u << 1 | 1, mid + 1, r);
pushup(u);
}
void tomax(int L, int R, int v, int u = 1, int l = 1, int r = n) {
if (R < l || r < L || t[u].mn >= v) return;
if (L <= l && r <= R && t[u].mn2 > v) return push_max(u, v);
int mid = (l + r) >> 1;
pushdown(u, l, r);
tomax(L, R, v, u << 1, l, mid), tomax(L, R, v, u << 1 | 1, mid + 1, r);
pushup(u);
}
long long qsum(int L, int R, int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return 0;
if (L <= l && r <= R) return t[u].sum;
int mid = (l + r) >> 1;
pushdown(u, l, r);
return qsum(L, R, u << 1, l, mid) + qsum(L, R, u << 1 | 1, mid + 1, r);
}
long long qmax(int L, int R, int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return -INF;
if (L <= l && r <= R) return t[u].mx;
int mid = (l + r) >> 1;
pushdown(u, l, r);
return max(qmax(L, R, u << 1, l, mid), qmax(L, R, u << 1 | 1, mid + 1, r));
}
long long qmin(int L, int R, int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return INF;
if (L <= l && r <= R) return t[u].mn;
int mid = (l + r) >> 1;
pushdown(u, l, r);
return min(qmin(L, R, u << 1, l, mid), qmin(L, R, u << 1 | 1, mid + 1, r));
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
build();
cin >> m;
for (int i = 1; i <= m; i++) {
int op, l, r, x;
cin >> op >> l >> r;
if (op <= 3) cin >> x;
if (op == 1)
add(l, r, x);
else if (op == 2)
tomax(l, r, x);
else if (op == 3)
tomin(l, r, x);
else if (op == 4)
cout << qsum(l, r) << '\n';
else if (op == 5)
cout << qmax(l, r) << '\n';
else
cout << qmin(l, r) << '\n';
}
return 0;
}
吉老师证出来这个算法的复杂度是
Mzl loves segment tree
两个序列
- 对
做区间取 - 对
做区间取 - 对
做区间加 - 询问
的区间和
每次操作完后,如果
先考虑最容易的区间加操作。只要
对于区间取最值的操作,你发现你打标记与下传标记是与
我们把区间
接下来需要考虑在 pushup 时如何维护
- 当左儿子的
最大值与当前节点的 最大值均相等时,左儿子的 会分别对当前节点的 产生贡献。 - 当左儿子的
最大值与当前节点 最大值相等,但是 最大值不相等时,左儿子的 会对该节点的 产生贡献, 会对该节点的 产生贡献。 - 当左儿子的
最大值与当前节点 最大值不相等,但是 最大值相等时,左儿子的 会对该节点的 产生贡献, 会对该节点的 产生贡献。 - 当左儿子的
最大值与当前节点的 最大值均不相等时,左儿子的 仅会对该节点的 产生贡献。
区间查询结果的
由于需要同时维护区间
#include <algorithm>
#include <cstdint>
#include <iostream>
using i64 = int64_t;
constexpr int N = 300010;
constexpr i64 INF = 1145141919810114514ll;
int n, q, op, l, r;
int a[N], b[N];
int lc(int u) { return (u << 1); }
int rc(int u) { return (u << 1) | 1; }
struct node {
i64 mx_ab[2][2]; // 0: not max A or B; 1: is max A or B
i64 max_val() {
return std::max({mx_ab[0][0], mx_ab[0][1], mx_ab[1][0], mx_ab[1][1]});
}
i64 mx1[2], mx2[2]; // 0: A; 1: B
i64 tag_add[2], tag_mn[2];
void clr_tag() { tag_add[0] = tag_add[1] = 0, tag_mn[0] = tag_mn[1] = INF; }
void init(i64 a, i64 b) {
mx1[0] = a, mx1[1] = b, mx2[0] = mx2[1] = -INF;
mx_ab[1][1] = a + b, mx_ab[0][1] = mx_ab[1][0] = mx_ab[0][0] = -INF;
}
void modify_mn(i64 tg, bool id) // id 0: A; 1: B
{
if (mx1[id] <= tg) return;
mx_ab[1][1] += (mx_ab[1][1] > -INF) ? (tg - mx1[id]) : 0;
mx_ab[!id][id] += (mx_ab[!id][id] > -INF) ? (tg - mx1[id]) : 0;
mx1[id] = tag_mn[id] = tg;
}
void modify_add(i64 tg, bool id) // id 0: A; 1: B
{
for (int i = 0; i < 2; ++i)
for (int j = 0; j < 2; ++j) mx_ab[i][j] += (mx_ab[i][j] > -INF) ? tg : 0;
mx1[id] += tg, mx2[id] += (mx2[id] > -INF) ? tg : 0;
tag_mn[id] += (tag_mn[id] < INF) ? tg : 0, tag_add[id] += tg;
}
void pushup(const node& l, const node& r) {
for (int i = 0; i < 2; ++i) {
if (l.mx1[i] == r.mx1[i])
mx1[i] = l.mx1[i], mx2[i] = std::max(l.mx2[i], r.mx2[i]);
else if (l.mx1[i] > r.mx1[i])
mx1[i] = l.mx1[i], mx2[i] = std::max(l.mx2[i], r.mx1[i]);
else
mx1[i] = r.mx1[i], mx2[i] = std::max(l.mx1[i], r.mx2[i]);
}
if (l.mx1[0] == mx1[0] && l.mx1[1] == mx1[1]) {
mx_ab[1][1] = l.mx_ab[1][1], mx_ab[0][1] = l.mx_ab[0][1];
mx_ab[1][0] = l.mx_ab[1][0], mx_ab[0][0] = l.mx_ab[0][0];
} else if (l.mx1[0] == mx1[0] && l.mx1[1] != mx1[1]) {
mx_ab[1][1] = mx_ab[0][1] = -INF;
mx_ab[1][0] = std::max(l.mx_ab[1][1], l.mx_ab[1][0]);
mx_ab[0][0] = std::max(l.mx_ab[0][1], l.mx_ab[0][0]);
} else if (l.mx1[0] != mx1[0] && l.mx1[1] == mx1[1]) {
mx_ab[1][1] = mx_ab[1][0] = -INF;
mx_ab[0][1] = std::max(l.mx_ab[1][1], l.mx_ab[0][1]);
mx_ab[0][0] = std::max(l.mx_ab[1][0], l.mx_ab[0][0]);
} else {
mx_ab[1][1] = mx_ab[0][1] = mx_ab[1][0] = -INF;
mx_ab[0][0] = std::max(
{l.mx_ab[0][0], l.mx_ab[0][1], l.mx_ab[1][0], l.mx_ab[1][1]});
}
if (r.mx1[0] == mx1[0] && r.mx1[1] == mx1[1]) {
mx_ab[1][1] = std::max(mx_ab[1][1], r.mx_ab[1][1]);
mx_ab[0][1] = std::max(mx_ab[0][1], r.mx_ab[0][1]);
mx_ab[1][0] = std::max(mx_ab[1][0], r.mx_ab[1][0]);
mx_ab[0][0] = std::max(mx_ab[0][0], r.mx_ab[0][0]);
} else if (r.mx1[0] == mx1[0] && r.mx1[1] != mx1[1]) {
mx_ab[1][0] = std::max({mx_ab[1][0], r.mx_ab[1][1], r.mx_ab[1][0]});
mx_ab[0][0] = std::max({mx_ab[0][0], r.mx_ab[0][1], r.mx_ab[0][0]});
} else if (r.mx1[0] != mx1[0] && r.mx1[1] == mx1[1]) {
mx_ab[0][1] = std::max({mx_ab[0][1], r.mx_ab[1][1], r.mx_ab[0][1]});
mx_ab[0][0] = std::max({mx_ab[0][0], r.mx_ab[1][0], r.mx_ab[0][0]});
} else
mx_ab[0][0] = std::max({mx_ab[0][0], r.mx_ab[0][0], r.mx_ab[0][1],
r.mx_ab[1][0], r.mx_ab[1][1]});
}
} tr[N << 2];
void pushup(int u) { tr[u].pushup(tr[lc(u)], tr[rc(u)]); }
void pushdown(int u) {
for (int i = 0; i < 2; ++i)
if (tr[u].tag_add[i])
tr[lc(u)].modify_add(tr[u].tag_add[i], i),
tr[rc(u)].modify_add(tr[u].tag_add[i], i);
for (int i = 0; i < 2; ++i)
if (tr[u].tag_mn[i] < INF)
tr[lc(u)].modify_mn(tr[u].tag_mn[i], i),
tr[rc(u)].modify_mn(tr[u].tag_mn[i], i);
tr[u].clr_tag();
}
void _build(int u, int L, int R) {
tr[u].clr_tag();
if (L == R) return tr[u].init(a[L], b[L]);
int M = (L + R) >> 1;
_build(lc(u), L, M), _build(rc(u), M + 1, R), pushup(u);
}
void build() { _build(1, 1, n); }
void _modify_mn(int u, int l, int r, int L, int R, int v, bool i) {
if (L > r || R < l || tr[u].mx1[i] <= v) return;
if (l <= L && R <= r && tr[u].mx2[i] < v) return tr[u].modify_mn(v, i);
pushdown(u);
int M = (L + R) >> 1;
_modify_mn(lc(u), l, r, L, M, v, i), _modify_mn(rc(u), l, r, M + 1, R, v, i);
pushup(u);
}
void modify_mn(int l, int r, int v, bool i) { _modify_mn(1, l, r, 1, n, v, i); }
void _modify_add(int u, int l, int r, int L, int R, int v, bool i) {
if (L > r || R < l) return;
if (l <= L && R <= r) return tr[u].modify_add(v, i);
pushdown(u);
int M = (L + R) >> 1;
_modify_add(lc(u), l, r, L, M, v, i),
_modify_add(rc(u), l, r, M + 1, R, v, i);
pushup(u);
}
void modify_add(int l, int r, int v, bool i) {
_modify_add(1, l, r, 1, n, v, i);
}
node _query(int u, int l, int r, int L, int R) {
if (l <= L && R <= r) return tr[u];
pushdown(u);
int M = (L + R) >> 1;
if (r <= M) return _query(lc(u), l, r, L, M);
if (l > M) return _query(rc(u), l, r, M + 1, R);
node ret;
node _l = _query(lc(u), l, M, L, M), _r = _query(rc(u), M + 1, r, M + 1, R);
ret.pushup(_l, _r);
return ret;
}
node query(int l, int r) { return _query(1, l, r, 1, n); }
using std::cin;
using std::cout;
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> q;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i) cin >> b[i];
build();
while (q--) {
cin >> op >> l >> r;
int x;
switch (op) {
case 1:
cin >> x;
modify_mn(l, r, x, 0);
break;
case 2:
cin >> x;
modify_mn(l, r, x, 1);
break;
case 3:
cin >> x;
modify_add(l, r, x, 0);
break;
case 4:
cin >> x;
modify_add(l, r, x, 1);
break;
case 5:
cout << query(l, r).max_val() << '\n';
break;
default:
break;
}
}
}
小结
在第本章节中我们给出了四道例题,分别讲解了基本区间最值操作的维护、多个标记的优先级处理、数集分类的思想以及多个分类的维护。本质上处理区间最值的基本思想就是数集信息的分类维护与高效合并。在下一章节中,我们将探讨历史区间最值的相关问题。
历史最值问题
历史最值不等于可持久化
注意,本章所讲到的历史最值问题不同于所谓的可持久化数据结构。这类特殊的问题我们将其称为历史最值问题。历史最值的问题可以分为三类。
历史最大值
简单地说,一个位置的历史最大值就是当前位置下曾经出现过的数的最大值。形式化地定义,我们定义一个辅助数组
这时,我们将
历史最小值
定义与历史最大值类似,在
历史版本和
辅助数组
我们称
接下来,我们将历史最值问题分成四类讨论。
可以用标记处理的问题
我们先不考虑操作 1。那么只有区间加的操作,我们维护标记
这个定义可能比较模糊。因此我们先解释一下标记的生存周期。一个标记会经历这样的过程:
- 在结点
被建立。 - 在结点
接受若干个新的标记的同时,与新的标记合并(指同类标记) - 结点
的标记下传给 的儿子, 的标记清空
我们认为在这个过程中,从 1 开始到 3 之前,都是结点
为什么要定义生存周期?利用这个概念,我们可以证明:在一个结点标记的生存周期内,其子结点均不会发生任何变化,并保留在这个生存周期之前的状态。道理很简单,因为在这个期间你是没有下传标记的。
于是,你就可以保证,在当前标记生存周期内的历史
那么信息的更新也是类似的,拿对应的标记更新即可。
接下来,我们考虑操作 1。
区间覆盖操作,会把所有的数变成一个数。在这之后,无论是区间加减还是覆盖,整个区间的数仍是同一个(除非你结束当前标记的生存周期,下传标记)。因此我们可以把第一次区间覆盖后的所有标记都看成区间覆盖标记。也就是说一个标记的生存周期被大致分成两个阶段:
- 若干个加减操作标记的合并,没有接收过覆盖标记。
- 覆盖操作的标记,没有所谓的加减标记(加减标记转化为覆盖标记)
于是我们把这个结点的 Pre 标记拆成
#include <algorithm>
#include <climits>
#include <iostream>
using namespace std;
using ll = long long;
constexpr int N = 1e5 + 7;
struct Tree {
int mx, _mx; // 区间最大值 区间历史最大值
int ad, _ad; // 区间加标记 区间阶段历史最大加标记
int st, _st; // 区间修改值 区间阶段历史最大修改标记
} g[N * 4];
int a[N];
int n, m;
#define ls u * 2
#define rs u * 2 + 1
#define mid (l + r) / 2
void pushup(int u) {
g[u].mx = max(g[ls].mx, g[rs].mx);
g[u]._mx = max(g[ls]._mx, g[rs]._mx);
}
void pushadd(int u, int v, int _v) {
g[u]._mx = max(g[u]._mx, g[u].mx + _v), g[u].mx += v;
if (g[u].st == INT_MIN)
g[u]._ad = max(g[u]._ad, g[u].ad + _v), g[u].ad += v;
else
g[u]._st = max(g[u]._st, g[u].st + _v), g[u].st += v;
}
void pushset(int u, int v, int _v) {
g[u]._mx = max(g[u]._mx, _v), g[u].mx = v;
g[u]._st = max(g[u]._st, _v), g[u].st = v;
}
void pushdown(int u, int l, int r) {
if (g[u].ad || g[u]._ad)
pushadd(ls, g[u].ad, g[u]._ad), pushadd(rs, g[u].ad, g[u]._ad),
g[u].ad = g[u]._ad = 0;
if (g[u].st != INT_MIN || g[u]._st != INT_MIN)
pushset(ls, g[u].st, g[u]._st), pushset(rs, g[u].st, g[u]._st),
g[u].st = g[u]._st = INT_MIN;
}
void build(int u = 1, int l = 1, int r = n) {
g[u]._st = g[u].st = INT_MIN;
if (l == r) {
g[u].mx = a[l];
g[u]._mx = a[l];
return;
}
build(ls, l, mid), build(rs, mid + 1, r);
pushup(u);
}
int L, R;
void add(int v, int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return;
if (L <= l && r <= R) return pushadd(u, v, max(v, 0));
pushdown(u, l, r);
add(v, ls, l, mid), add(v, rs, mid + 1, r);
pushup(u);
}
void tset(int v, int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return;
if (L <= l && r <= R) return pushset(u, v, v);
pushdown(u, l, r);
tset(v, ls, l, mid), tset(v, rs, mid + 1, r);
pushup(u);
}
int qmax(int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return INT_MIN;
if (L <= l && r <= R) return g[u].mx;
pushdown(u, l, r);
return max(qmax(ls, l, mid), qmax(rs, mid + 1, r));
}
int qmaxh(int u = 1, int l = 1, int r = n) {
if (R < l || r < L) return INT_MIN;
if (L <= l && r <= R) return g[u]._mx;
pushdown(u, l, r);
return max(qmaxh(ls, l, mid), qmaxh(rs, mid + 1, r));
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
build();
int m, z;
cin >> m;
for (int i = 1; i <= m; ++i) {
char op;
cin >> op;
while (op == ' ' || op == '\r' || op == '\n') cin >> op;
cin >> L >> R;
int x;
if (op == 'Q')
cout << qmax() << '\n';
else if (op == 'A')
cout << qmaxh() << '\n';
else if (op == 'P')
cin >> x, add(x);
else
cin >> x, tset(x);
}
return 0;
}
创建日期: 2019年7月11日