【题目链接】
【思路要点】
- 首先定义一种标记\((a,b)\)表示先给这个区间的数加上\(a\)再和\(b\)取最大值。
- 不难发现题目中的操作均可以表示成\((a,b)\)的形式,分别是\((x,0),(-x,0),(-INF,x)\)。
- 考虑合并标记\((a,b),(c,d)\),那么得到的是\((max(a+c,-INF),max(b+c,d))\),对于4号询问,我们只需要将标记依次下传至叶子结点即可回答询问。
- 现在考虑历史最大值标记,我们可以把标记看成一个函数,每一个数字对应的函数值表示该数字作用该标记后的答案。不难发现该函数是一个分段函数,由两段斜率分别为0和1的直线组成。“历史最大值”标记定义为“从上次下传该点标记至今,标记达到的最大情况”,其中“最大情况”对于每一个数字是独立的。
- 在更新历史最大值标记时,我们相当于将两个函数画出来,取靠上方的一段。下图展示的是一种可能的情况,其中蓝色部分代表合并的函数,红色部分代表合并的结果。
- 因此,该标记同样可以合并,在询问5时只需将标记下传至叶子结点即可回答询问。
- 时间复杂度\(O(MLogN)\)。
- 题解原文见国家集训队2016论文集之《区间最值操作与历史最值问题——杭州学军中学 吉如一》。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 500005; const long long INF = 1e18; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct info {long long Add, Max; }; info operator * (info a, info b) { info ans; ans.Add = max(a.Add + b.Add, -INF); ans.Max = max(a.Max + b.Add, b.Max); return ans; } info operator + (info a, info b) { info ans; ans.Add = max(a.Add, b.Add); ans.Max = max(a.Max, b.Max); return ans; } long long operator * (int a, info b) { return max(a + b.Add, b.Max); } struct Segment_Tree { struct Node { int lc, rc; info now, his; } a[MAXN * 2]; int n, root, size, val[MAXN]; void build(int &root, int l, int r) { root = ++size; if (l == r) return; int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); } void init(int x) { n = x; root = size = 0; build(root, 1, n); } void pushdown(int root) { a[a[root].lc].his = a[a[root].lc].his + a[a[root].lc].now * a[root].his; a[a[root].lc].now = a[a[root].lc].now * a[root].now; a[a[root].rc].his = a[a[root].rc].his + a[a[root].rc].now * a[root].his; a[a[root].rc].now = a[a[root].rc].now * a[root].now; a[root].now = (info) {0, 0}; a[root].his = (info) {0, 0}; } void add(int root, int l, int r, int ql, int qr, info x) { if (l == ql && r == qr) { a[root].his = a[root].his + a[root].now * x; a[root].now = a[root].now * x; return; } pushdown(root); int mid = (l + r) / 2; if (mid >= ql) add(a[root].lc, l, mid, ql, min(mid, qr), x); if (mid + 1 <= qr) add(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, x); } void add(int l, int r, long long x, long long y) { add(root, 1, n, l, r, (info) {x, y}); } long long query(int root, int l, int r, int x) { if (l == r) return val[x] * a[root].now; pushdown(root); int mid = (l + r) / 2; if (mid >= x) return query(a[root].lc, l, mid, x); else return query(a[root].rc, mid + 1, r, x); } long long query(int x) { return query(root, 1, n, x); } long long history(int root, int l, int r, int x) { if (l == r) return val[x] * a[root].his; pushdown(root); int mid = (l + r) / 2; if (mid >= x) return history(a[root].lc, l, mid, x); else return history(a[root].rc, mid + 1, r, x); } long long history(int x) { return history(root, 1, n, x); } } ST; int main() { int n, m; read(n), read(m); for (int i = 1; i <= n; i++) read(ST.val[i]); ST.init(n); for (int i = 1; i <= m; i++) { int opt; read(opt); if (opt <= 3) { int l, r, x; read(l), read(r), read(x); if (opt == 1) ST.add(l, r, x, 0); if (opt == 2) ST.add(l, r, -x, 0); if (opt == 3) ST.add(l, r, -INF, x); } else { int x; read(x); if (opt == 4) writeln(ST.query(x)); if (opt == 5) writeln(ST.history(x));; } } return 0; }