题意
你需要写一个数据结构,需要支持对一个长度为\(n\)的数组进行下面四种操作\(m\)次
- \(\tt{C \ l \ r \ d}\):区间\([l,r]\)中的数都加\(d\),同时新建一个历史版本\(T+1\)
- \(\tt{Q \ l \ r}\):查询当前版本的区间\([l,r]\)中所有数的和
- \(\tt {H \ l \ r \ t}\):查询第\(t\)个历史版本的区间\([l,r]\)的和
- \(\tt{B \ t}\):将当前的数组回溯至版本\(t\),同时将\(T\)赋值为\(t\)
初始版本\(T=0\)
解法
主席树模板
这里主要是介绍线段树中标记永久化在可持久化数据结构中的应用
在可持久化数据结构中,我们如果还用可以下放的懒标记将会变得十分麻烦
这时候我们需要一种标记方式能够省去下放的过程
这就是标记永久化
怎样进行标记永久化操作呢?
对于修改操作,若我们要修改区间\([l,r]\),我们就把所有与\([l,r]\)有交的区间全部更新为受到标记影响后的。而对于被\([l,r]\)包含的第一级区间,我们就打上标记
对于查询操作,我们只需要把从根到我们要询问的区间这一路上所有的标记影响全部累加起来,统计答案再加上去即可。这样子,对于所有的区间,覆盖它的标记都逃不掉。
(注:部分引用于\(AKMer\),浅谈标记永久化)
我理解为,对于标记下放的方法,实际上就是每次把标记所在的下界降低,不断的向子区间推
而标记永久化这是设置了永远不变的下界,由于规定顺序是向上进行累加,所以下界不用再向下推了
代码
#include <cstdio>
#include <cctype>
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int read();
int n, m, T;
int a[N], rt[N];
inline int min(int x, int y) {
return x < y ? x : y;
}
inline int max(int x, int y) {
return x > y ? x : y;
}
struct CTree {
int sz;
int ls[N * 50], rs[N * 50];
long long sum[N * 50], tag[N * 50];
void clear() { sz = 0; }
int newnode() {
++sz;
ls[sz] = rs[sz] = sum[sz] = tag[sz] = 0;
return sz;
}
void copy(int x, int y) {
ls[x] = ls[y], rs[x] = rs[y], sum[x] = sum[y], tag[x] = tag[y];
}
void build(int &x, int l, int r) {
x = newnode();
if (l == r)
return sum[x] = a[l], void();
int mid = l + r >> 1;
build(ls[x], l, mid), build(rs[x], mid + 1, r);
sum[x] = sum[ls[x]] + sum[rs[x]];
}
void mktree(int &x, int y, int l, int r, int ql, int qr, int v) {
copy(x = newnode(), y);
if (ql <= l && r <= qr)
return tag[x] += v, void();
int mid = l + r >> 1;
if (ql <= mid)
mktree(ls[x], ls[y], l, mid, ql, qr, v);
if (qr > mid)
mktree(rs[x], rs[y], mid + 1, r, ql, qr, v);
sum[x] += 1LL * (min(r, qr) - max(l, ql) + 1) * v;
}
long long query(int x, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr)
return tag[x] * (r - l + 1) + sum[x];
int mid = l + r >> 1;
long long res = 1LL * (min(r, qr) - max(l, ql) + 1) * tag[x];
if (ql <= mid)
res += query(ls[x], l, mid, ql, qr);
if (qr > mid)
res += query(rs[x], mid + 1, r, ql, qr);
return res;
}
} tr;
int main() {
n = read(), m = read();
for (int i = 1; i <= n; ++i) a[i] = read();
tr.build(rt[T], 1, n);
char a[5];
int l, r, d;
for (int i = 1; i <= m; ++i) {
scanf("%s", a);
if (a[0] == 'C') {
++T;
l = read(), r = read(), d = read();
tr.mktree(rt[T], rt[T - 1], 1, n, l, r, d);
}
if (a[0] == 'Q') {
l = read(), r = read();
printf("%lld\n", tr.query(rt[T], 1, n, l, r));
}
if (a[0] == 'H') {
l = read(), r = read(), d = read();
printf("%lld\n", tr.query(rt[d], 1, n, l, r));
}
if (a[0] == 'B')
T = read();
}
return 0;
}
int read() {
int x = 0, f = 1, c = getchar();
while (!isdigit(c)) c == '-' ? f = -1, c = getchar() : c = getchar();
while (isdigit(c)) x = x * 10 + c - 48, c = getchar();
return x * f;
}