文章的标题为什么要取得这么长?(逃
http://acmoj.shu.edu.cn/problem/422/
给定一个数 n(1<=n<=105) ,以及区间 [1,n] 上每个数的初始值,接下来有 q(1<=q<=105) 次操作,操作有两种类型,格式分别为:
1 L R X,表示对 [L,R] 区间上所有数加 x(−104<=x<=104) 2 A,询问A点的历史上最大绝对值可以这样看问题,记 [1,n] 上初始值为 a[i] ,一开始区间 [1,n] 上的所有值都为0,然后往 [L,R] 上不断加x,有个数据结构可以保存某个区间上的历史最大值 mx 和历史最小值 mn ,于是每次询问的答案就是 max(abs(a[i]+mx[i]),abs(a[i]+mn[i])) 。
很容易可以想到这个可以用线段树来保存区间上的历史最大和最小值。难点就是如何分配线段树上的各个值以及相关操作的执行方式。
用 laz[] 数组表示一个区间节点要向其子节点传播的修改量,用 mx[] 数组表示 laz[] 数组历史上的最大值,同理, mn[] 为最小值。那么对一个父节点 y ,其子节点x受到这样的影响: laz[x]+=laz[y] ,区间y把自身信息向其子区间x传播。 mx[x]=max(mx[x],laz[x]+mx[y]) , laz[x] 的历史最大值就是其父节点y的 laz[y] 的历史最大值对x的影响, mn[x] 同理。
特别注意的一点是:每次向下传播(pushDown())后, laz,mx,mn 这些数组都要清空,是的,这是个很基础的线段树注意点,但是本渣渣就错在了这里QAQ。因为mx和mn记录的是laz的历史最值,所以,laz被清空时,mx,mn一定要被清空,这样才能保证操作的正确性。
8.21.UPD:最近发生了好多事,人很丧,博客都几百年没更新了,这题还有种虽然比较复杂,但是更值得学习一下的做法,就是用扫描线的思想以时间建立线段树,离线按序号大小处理各个节点的变化,大致思路就是这样,详见代码。
扫描线离线代码
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define ll rt << 1 #define rr rt << 1 | 1 #define lson l, mid, ll #define rson mid + 1, r, rr #define abs(x) ((x) < 0 ? -(x) : (x)) typedef pair<int, int> P; const int N = 1e5 + 5; const int INF = 0x3f3f3f3f; struct OP { int pos, op, val, tim; bool operator<(const OP& r) const { return pos < r.pos || (pos == r.pos && op < r.op); } }; int n, m; int a[N], ans[N]; int tot, tol; int mx[N << 2], mn[N << 2], laz[N << 2]; OP p[N << 1]; inline void gm(P &a, P b) { if (a.first < b.first) a.first = b.first; if (a.second > b.second) a.second = b.second; } inline void pushDown(int rt) { mx[ll] += laz[rt]; mn[ll] += laz[rt]; laz[ll] += laz[rt]; mx[rr] += laz[rt]; mn[rr] += laz[rt]; laz[rr] += laz[rt]; laz[rt] = 0; } inline void pushUp(int rt) { mx[rt] = max(mx[ll], mx[rr]); mn[rt] = min(mn[ll], mn[rr]); } void build(int l, int r, int rt) { mx[rt] = mn[rt] = laz[rt] = 0; if (l == r) return ; int mid = (l + r) >> 1; build(lson); build(rson); } void add(int L, int R, int V, int l, int r, int rt) { if (L <= l && r <= R) { mx[rt] += V; mn[rt] += V; laz[rt] += V; return ; } if (laz[rt]) pushDown(rt); int mid = (l + r) >> 1; if (L <= mid) add(L, R, V, lson); if (mid < R) add(L, R, V, rson); pushUp(rt); } P query(int L, int R, int l, int r, int rt) { if (L <= l && r <= R) { return P(mx[rt], mn[rt]); } if (laz[rt]) pushDown(rt); int mid = (l + r) >> 1; P ret = P(-INF, INF); if (L <= mid) gm(ret, query(L, R, lson)); if (mid < R) gm(ret, query(L, R, rson)); return ret; } int main() { int T; scanf("%d", &T); while (T--) { scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) scanf("%d", a + i); tol = tot = 0; int op, l, r, x; for (int i = 1; i <= m; ++i) { scanf("%d", &op); if (op == 1) { scanf("%d%d%d", &l, &r, &x); p[tot].op = 0; p[tot].pos = l; p[tot].tim = i; p[tot].val = x; ++tot; p[tot].op = 0; p[tot].pos = r + 1; p[tot].tim = i; p[tot].val = -x; ++tot; } else { scanf("%d", &x); p[tot].op = 1; p[tot].pos = x; p[tot].tim = i; p[tot].val = tol++; ++tot; } } sort(p, p + tot); build(1, m, 1); P ret; for (int i = 0; i < tot; ++i) { if (p[i].op) { ret = query(1, p[i].tim, 1, m, 1); ans[p[i].val] = max(max(abs(a[p[i].pos] + ret.first), abs(a[p[i].pos] + ret.second)), abs(a[p[i].pos])); } else { add(p[i].tim, m, p[i].val, 1, m, 1); } } for (int i = 0; i < tol; ++i) printf("%d\n", ans[i]); } }