HDU 4348 To the moon - 主席树 + 区间修改 + 标记永久化 (理解标记永久化操作)
- 区间\([l,r]\)加上\(d\)并更新版本
- 查询当前版本\([l,r]\)区间的和
- 查询\(t\)版本的\([l,r]\)的区间和
- 回溯到历史版本\(t\)
可持久化,就用可持久化线段树去操作。
主席树其实不止可以用于权值线段树,当使用线段树时,也可以操作的。
但是如果用线段树的区间操作里面,使用lazy标记去进行区间修改,是不对的。
标记永久化:标记永久化,顾名思义,指标记一旦被打上,就不再下传或清空。而是在询问的过程中计算每个遇到结点对当前询问的影响。
因为对于主席树,每次修改操作我们在之前基础上建一棵新树,但是他们的一些儿子是共用的,如果直接用线段树的lazy标记pushdown一下,之前的版本也会受到影响。而我们的想法就是只修改当前版本的标记。标记不用pushdown,自然也不用pushup(开始建树的时候,可能要pushup)
其实永久性标记相当于是在每次修改,都会进行更新操作,但也加入了一个lazy标记,而在区间查询时,不断递归,同时也需要更新一个权值,代表我要找的区间需要增加的标记值。
标记永久化与惰性标记的最大区别在于是否去向下传递。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 5;
int a[N];
struct Chairman_tree{
int lc[N << 5], rc[N << 5], rt[N], tot;
ll sum[N << 5], lazy[N << 5];
void build(int &now, int l, int r){
now = ++tot;
lazy[now] = lc[now] = rc[now] = 0;
if(l == r) {
sum[now] = a[l];
return;
}
int mid = (l + r) >> 1;
build(lc[now], l, mid); build(rc[now], mid + 1, r);
sum[now] = sum[lc[now]] + sum[rc[now]];
}
void Update(int &now, int pre, int L, int R, int l, int r, ll val){
now = ++tot;
lc[now] = lc[pre], rc[now] = rc[pre], lazy[now] = lazy[pre];
sum[now] = sum[pre] + (min(R, r) - max(L, l) + 1) * val;
if(l <= L && R <= r) {
lazy[now] += val;
return;
}
int mid = (L + R) >> 1;
if(l <= mid) Update(lc[now], lc[pre], L, mid, l, r, val);
if(r > mid) Update(rc[now], rc[pre], mid + 1, R, l, r, val);
}
ll Query(int now, int L, int R, int l, int r, ll cc){
if(l <= L && R <= r) return sum[now] + cc * (R - L + 1);
int mid = (L + R) >> 1;
ll ans = 0;
if(l <= mid) ans += Query(lc[now], L, mid, l, r, cc + lazy[now]);
if(r > mid) ans += Query(rc[now], mid + 1, R, l, r, cc + lazy[now]);
return ans;
}
} ct;
int main(){
int n, m;
while(~scanf("%d%d", &n, &m)){
ct.tot = 0;
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
ct.build(ct.rt[0], 1, n);
int nowhis = 0;
for(int i = 1; i <= m; i++) {
char op[10]; int l, r, add, his;
scanf("%s", op);
if(op[0] == 'Q') {
scanf("%d%d", &l, &r);
printf("%lld\n", ct.Query(ct.rt[nowhis], 1, n, l, r, 0));
}
if(op[0] == 'C') {
scanf("%d%d%d", &l, &r, &add);
nowhis++;
ct.Update(ct.rt[nowhis], ct.rt[nowhis - 1], 1, n, l, r, add);
}
if(op[0] == 'H') {
scanf("%d%d%d", &l, &r, &his);
printf("%lld\n", ct.Query(ct.rt[his], 1, n, l, r, 0));
}
if(op[0] == 'B') {
scanf("%d", &nowhis);
}
}
}
return 0;
}
I‘m Stein, welcome to my blog