一句话题意:写一个支持区间修改,区间求和的可持久化数据结构。
考虑使用主席树,然而,区间修改怎么办?
似乎有标记永久化的方法。
对于线段树上完全覆盖标记产生贡献的区间,我们直接打上一个$tag$,而对于不完全产生贡献但是会产生贡献的区间,我们直接把贡献累加到$sum$里面去。
查询的时候从上往下走一走,顺便算一算这个结点的$tag$会对答案产生多少贡献。
我们知道任何一条线段(长度为$n$)会被拆成不超过$logn$级别的小的长度为$2^k$长的线段,这样子我们每一次修改新建的结点也是$log$级别的。
并不会算空间。尽量开大。
时间复杂度当然是$O((n + m)logn)$。
Code:
#include <cstdio> #include <cstring> using namespace std; typedef long long ll; const int N = 1e5 + 5; int n, qn; ll a[N]; template <typename T> inline void read(T &X) { X = 0; char ch = 0; T op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline int max(int x, int y) { return x > y ? x : y; } inline int min(int x, int y) { return x > y ? y : x; } namespace PSegT { struct Node { int lc, rc; ll sum, tag; } s[N * 600]; int root[N], nodeCnt = 0; #define lc(p) s[p].lc #define rc(p) s[p].rc #define sum(p) s[p].sum #define tag(p) s[p].tag #define mid ((l + r) >> 1) inline void up(int p) { if(p) sum(p) = sum(lc(p)) + sum(rc(p)); } void build(int &p, int l, int r) { p = ++nodeCnt, tag(p) = 0LL; if(l == r) { sum(p) = a[l]; return; } build(lc(p), l, mid); build(rc(p), mid + 1, r); up(p); } void modify(int &p, int l, int r, int x, int y, ll v, int pre) { s[p = ++nodeCnt] = s[pre]; if(x <= l && y >= r) { tag(p) += 1LL * v; return; } else { int len = min(r, y) - max(l, x) + 1; sum(p) += 1LL * len * v; } if(x <= mid) modify(lc(p), l, mid, x, y, v, lc(pre)); if(y > mid) modify(rc(p), mid + 1, r, x, y, v, rc(pre)); } ll query(int p, int l, int r, int x, int y) { if(x <= l && y >= r) return sum(p) + 1LL * (r - l + 1) * tag(p); int len = min(r, y) - max(l, x) + 1; ll res = 1LL * len * tag(p); if(x <= mid) res += query(lc(p), l, mid, x, y); if(y > mid) res += query(rc(p), mid + 1, r, x, y); return res; } } using namespace PSegT; int main() { // freopen("Sample.txt", "r", stdin); for(; scanf("%d%d", &n, &qn) != EOF; ) { // read(n), read(qn); for(int i = 1; i <= n; i++) read(a[i]); memset(root, 0, sizeof(root)); nodeCnt = 0; int now = 0; build(root[0], 1, n); /* for(int i = 1; i <= n; i++) printf("%lld ", query(root[0], 1, n, i, i)); printf(" "); */ for(int i = 1; i <= qn; i++) { char op[3]; scanf("%s", op); if(op[0] == 'C') { int x, y; read(x), read(y); ll v; read(v); ++now; modify(root[now], 1, n, x, y, v, root[now - 1]); } if(op[0] == 'Q') { int x, y; read(x), read(y); printf("%lld ", query(root[now], 1, n, x, y)); } if(op[0] == 'H') { int x, y, t; read(x), read(y), read(t); printf("%lld ", query(root[t], 1, n, x, y)); } if(op[0] == 'B') { int t; read(t); for(int j = t + 1; j <= now; j++) root[j] = 0; now = t; } } /* for(int t = 0; t <= tim; t++, printf(" ")) for(int i = 1; i <= n; i++) printf("%lld ", query(root[t], 1, n, 1, 2)); */ // printf(" "); for(int i = 1; i <= nodeCnt; i++) lc(i) = rc(i) = sum(i) = tag(i) = 0LL; } return 0; }