Forever Young

SP11470 TTM - To the moon

题意

一个长度为 \(n\) 的数组,\(4\) 种操作 :

  • C l r d:区间 \([l,r]\) 中的数都加 \(d\),同时当前的时间戳加 \(1\)
  • Q l r:查询当前时间戳区间 \([l,r]\)中所有数的和 。
  • H l r t:查询时间戳 \(t\) 区间 \([l,r]\) 的和 。
  • B t:将当前时间戳置为 \(t\)

简化题意:给定一个长度为 \(n\) 的数组,要求支持区间修改、查询某版本某个区间的和、版本回溯。

思路

可持久化线段树 + 标记永久化

这玩意儿不能叫做主席树 \(qwq\)

题意简述一下就是带修改的可持久化线段树。

普通的线段树在区间修改时可以打标记,用到子节点的时候再进行 pushdown 操作,但如果是可持久化线段树就不能这么做,否则就会因为下传标记影响与当前版本共用的点,因此引入了标记永久化,有了标记永久化,就可以轻松解决这道题了。

每次执行修改操作时,仍然对被修改区间覆盖的节点打上标记,但是不再下传,而是改为询问时直接计算,当询问时要往下走就把这个节点的标记计算出来,返回值时再把这些算出的标记的和加上。

注意:

  • 在询问时,\(val\) 的初值是 \(lazy[now]\times(R-L+1)\),而不是 \(lazy[now]\times(r-l+1)\),应该乘询问的长度而不是当前区间长度。

代码

/*
  Name: SP11470 TTM - To the moon
  Author: Loceaner
  Date: 07/09/20 17:31
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;

const int A = 1e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
  char c = getchar();
  int x = 0, f = 1;
  for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
  for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
  return x * f;
}

int n, m, root[A], tcnt, a[A];
struct tree { int ls, rs, sum, lazy; } t[A << 5];

void build(int &now, int l, int r) {
  now = ++tcnt;
  if (l == r) {
    t[now].sum = a[l];
    return;
  }
  int mid = (l + r) >> 1;
  build(t[now].ls, l, mid), build(t[now].rs, mid + 1, r);
  t[now].sum = t[t[now].ls].sum + t[t[now].rs].sum;
}

void update(int &now, int pre, int l, int r, int L, int R, int val) {
  t[++tcnt] = t[pre];
  now = tcnt;
  if (L <= l && r <= R) {
    t[now].lazy += val;
    return;
  }
  int mid = (l + r) >> 1;
  if (L <= mid) update(t[now].ls, t[pre].ls, l, mid, L, R, val);
  if (R > mid) update(t[now].rs, t[pre].rs, mid + 1, r, L, R, val);
  t[now].sum = t[t[now].ls].sum + t[t[now].rs].sum + t[t[now].ls].lazy * (mid - l + 1) + t[t[now].rs].lazy * (r - mid);
}

int query(int now, int l, int r, int L, int R) {
  if (L == l && r == R) return t[now].sum + t[now].lazy * (r - l + 1);
  int mid = (l + r) >> 1; int val = (R - L + 1) * t[now].lazy;
  if (R <= mid) return val + query(t[now].ls, l, mid, L, R);
  if (L > mid) return val + query(t[now].rs, mid + 1, r, L, R);
  return val + query(t[now].ls, l, mid, L, mid) + query(t[now].rs, mid + 1, r, mid + 1, R);
}

signed main() {
  n = read(), m = read();
  for (int i = 1; i <= n; i++) a[i] = read();
  build(root[0], 1, n);
  int x, y, val, tim, now = 0; char opt[2];
  while (m--) {
    scanf("%s", opt);
    if (opt[0] == 'C') {
      now++;
      x = read(), y = read(), val = read();
      update(root[now], root[now - 1], 1, n, x, y, val);
    }
    else if (opt[0] == 'Q') 
      x = read(), y = read(), cout << query(root[now], 1, n, x, y) << '\n';
    else if (opt[0] == 'H') 
      x = read(), y = read(), tim = read(), cout << query(root[tim], 1, n, x, y) << '\n'; 
    else if (opt[0] == 'B') now = read();
    else continue;
  }
  return 0;
}
posted @ 2020-09-07 21:28  Loceaner  阅读(148)  评论(2编辑  收藏  举报