1. 程式人生 > 實用技巧 >SP11470 TTM - To the moon

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;
}