【题目链接】
【思路要点】
- 题目中提到的操作均为区间操作,考虑使用线段树。
- 若不存在A询问,剩余部分属于线段树的基本操作。
- 考虑将题目先简化一下:若不存在C操作,如何用线段树解决本题?
- 首先,对于区间最大值询问,我们维护区间最大值\(Max\)和区间加标记\(tagadd\),分别表示对应区间的最大值和未下传的区间加标记值,其下传方式显然。
- 其次,对于区间历史最大值询问,我们维护区间历史最大值\(Mbx\)和区间加标记\(tbgadd\),分别表示对应区间的历史最大值和从上一次下传该区间标记至今,\(tagadd\)的最大值。
- 对一个区间\(i\)进行区间加时,我们代入两个参数\((Val,Vbl)\)分别表示区间加的真数以及历史最大数。具体来说,对于一个被区间加\(x\)定位的区间,代入参数\((x,x)\);对于一个正在进行标记下传的区间\(i\)的子区间,代入参数\((tagadd_{i},tbgadd_{i})\)。
- 在参数为\((Val,Vbl)\)的区间加操作后,维护区间\(i\)的各值的方式如下$$tbgadd_{i}=max(tbgadd_{i},tagadd_{i}+Vbl)$$$$Mbx_{i}=max(Mbx_{i},Max_{i}+Vbl)$$$$tagadd_{i}=tagadd_{i}+Val$$$$Max_{i}=Max_{i}+Val$$
- 至此,我们实现了在不存在C操作的情况下支持A询问。
- 回到原题,我们考虑一个被赋值后的区间,它的每一个数都是一样的,因此区间加减操作可以看做是区间赋值操作。更进一步,一个区间受到的修改可以分成两部分来看:在赋值操作之前加减操作对它的影响,记为\((tagadd,tbgadd)\);以及赋值操作之后赋值操作对它的影响,记为\((tagval,tbgval)\),分别表示当前区间中所有数的值,以及在赋值操作开始后区间中数出现过的最大值,显然我们还需要记录布尔变量\(type\)表示该区间是否被赋值过。
- 注意即使赋值操作开始,\((tagadd,tbgadd)\)也是有意义的,因为历史最大值可能出现在赋值操作开始之前。
- 在下传时,我们同样代入两个参数\((Val,Vbl)\)分别表示赋值操作的真数和历史最大数,注意对\(type\)不同的区间分别处理。由于下传较为繁琐,且其思想与上文类似,在此不再赘述,有需要的读者请自行阅读代码。
- 时间复杂度\(O(MLogN)\)。
- 题解原文见国家集训队2016论文集之《区间最值操作与历史最值问题——杭州学军中学 吉如一》。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; const int INF = INT_MAX; 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; } void getopt(char &x) { x = getchar(); while (x != 'A' && x != 'P' && x != 'C' && x != 'Q') x = getchar(); } 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 SegmentTree { struct Node { bool type; int lc, rc; int Max, tagadd, tagval; int Mbx, tbgadd, tbgval; } a[MAXN * 2]; int root, n, size, num[MAXN]; void update(int root) { a[root].Max = max(a[a[root].lc].Max, a[a[root].rc].Max); a[root].Mbx = max(a[a[root].lc].Mbx, a[a[root].rc].Mbx); } void chkmax(int &x, int y) { x = max(x, y); } void addtag(int root, int val, int vbl) { chkmax(a[root].tbgadd, a[root].tagadd + vbl); chkmax(a[root].Mbx, a[root].Max + vbl); a[root].tagadd += val; a[root].Max += val; } void valtag(int root, int val, int vbl) { if (a[root].type) { chkmax(a[root].tbgval, vbl); chkmax(a[root].Mbx, vbl); a[root].tagval = val; a[root].Max = val; } else { a[root].type = true; a[root].tbgval = vbl; chkmax(a[root].Mbx, vbl); a[root].tagval = val; a[root].Max = val; } } void pushdown(int root) { if (a[a[root].lc].type) valtag(a[root].lc, a[a[root].lc].tagval + a[root].tagadd, a[a[root].lc].tagval + a[root].tbgadd); else addtag(a[root].lc, a[root].tagadd, a[root].tbgadd); if (a[a[root].rc].type) valtag(a[root].rc, a[a[root].rc].tagval + a[root].tagadd, a[a[root].rc].tagval + a[root].tbgadd); else addtag(a[root].rc, a[root].tagadd, a[root].tbgadd); if (a[root].type) { valtag(a[root].lc, a[root].tagval, a[root].tbgval); valtag(a[root].rc, a[root].tagval, a[root].tbgval); } a[root].type = false; a[root].tagadd = a[root].tbgadd = 0; a[root].tagval = a[root].tbgval = 0; } void build(int &root, int l, int r) { root = ++size; if (l == r) { a[root].Max = a[root].Mbx = num[l]; return; } int mid = (l + r) / 2; build(a[root].lc, l, mid); build(a[root].rc, mid + 1, r); update(root); } void init(int x) { n = x; root = size = 0; build(root, 1, n); } int querya(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].Max; pushdown(root); int mid = (l + r) / 2, ans = -INF; if (mid >= ql) chkmax(ans, querya(a[root].lc, l, mid, ql, min(qr, mid))); if (mid + 1 <= qr) chkmax(ans, querya(a[root].rc, mid + 1, r, max(mid + 1, ql), qr)); return ans; } int querya(int l, int r) { return querya(root, 1, n, l, r); } int queryb(int root, int l, int r, int ql, int qr) { if (l == ql && r == qr) return a[root].Mbx; pushdown(root); int mid = (l + r) / 2, ans = -INF; if (mid >= ql) chkmax(ans, queryb(a[root].lc, l, mid, ql, min(qr, mid))); if (mid + 1 <= qr) chkmax(ans, queryb(a[root].rc, mid + 1, r, max(mid + 1, ql), qr)); return ans; } int queryb(int l, int r) { return queryb(root, 1, n, l, r); } void add(int root, int l, int r, int ql, int qr, int val) { if (l == ql && r == qr) { if (a[root].type) valtag(root, a[root].tagval + val, a[root].tagval + val); else addtag(root, val, val); return; } pushdown(root); int mid = (l + r) / 2; if (mid >= ql) add(a[root].lc, l, mid, ql, min(qr, mid), val); if (mid + 1 <= qr) add(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, val); update(root); } void add(int l, int r, int val) { add(root, 1, n, l, r, val); } void val(int root, int l, int r, int ql, int qr, int num) { if (l == ql && r == qr) { valtag(root, num, num); return; } pushdown(root); int mid = (l + r) / 2; if (mid >= ql) val(a[root].lc, l, mid, ql, min(qr, mid), num); if (mid + 1 <= qr) val(a[root].rc, mid + 1, r, max(mid + 1, ql), qr, num); update(root); } void val(int l, int r, int num) { val(root, 1, n, l, r, num); } } ST; int main() { int n; read(n); for (int i = 1; i <= n; i++) read(ST.num[i]); ST.init(n); int m; read(m); for (int i = 1; i <= m; i++) { char opt; getopt(opt); int l, r, val; read(l), read(r); if (l > r) swap(l, r); if (opt == 'Q') writeln(ST.querya(l, r)); if (opt == 'A') writeln(ST.queryb(l, r)); if (opt == 'P') read(val), ST.add(l, r, val); if (opt == 'C') read(val), ST.val(l, r, val); } return 0; }