Description

华华看书了解到,一起玩养成类的游戏有助于两人培养感情。所以他决定和月月一起种一棵树。因为华华现在也是信息学高手了,所以他们种的树是信息学意义下的。
华华和月月一起维护了一棵动态有根树,每个点有一个权值。刚开存档的时候,树上只有 0 号节点,权值为 0 。接下来有两种操作:
操作 1:输入格式1 i,表示月月氪金使节点 i 长出了一个新的儿子节点,权值为0,编号为当前最大编号 +1(也可以理解为,当前是第几个操作 1,新节点的编号就是多少)。
操作 2:输入格式 2 i a表示华华上线做任务使节点 i 的子树中所有节点(即它和它的所有子孙节点)权值加 a 。
但是月月有时会检查华华有没有认真维护这棵树,会作出询问:
询问 3:输入格式3 i,华华需要给出 i 节点此时的权值。
华华当然有认真种树了,不过还是希望能写个程序以备不时之需。

Solution

树上区间操作,考虑用dfs序表示成数组形式,那么就是我们熟悉的序列区间操作了。由于该问题的树会出现节点增加的情况,显然我们在序列操作的时候可以先假设该点已存在,最后计算贡献的时候减去该点的值即可。
那么问题就可以离线解决,先存储所有操作,建树后得到dfs序,接下来就是树状数组的区间加和单点求值了。

Code

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
const int mod = 1e9 + 7; 
const int N = 1e6 + 5;
int t[N], l[N], r[N], op[N], val[N], a[N], b[N], cnt = 1;
vector<int> G[N];
int lowbit(int x) {
  return x & (-x);
}
void update(int x, int add) {
  while(x < N) {
    t[x] += add;
    x += lowbit(x);
  }
}
int query(int x) {
  int res = 0;
  while(x) {
    res += t[x];
    x -= lowbit(x);
  }
  return res;
}
void dfs(int x, int par) {
  l[x] = ++cnt; // 最小编号
  for(int i = 0; i < G[x].size(); i++) {
    int u = G[x][i];
    dfs(u, x);
  }
  r[x] = cnt; // 最大编号
}
int main() {
  int m; cin >> m;
  for(int i = 1; i <= m; i++) {
    cin >> op[i] >> a[i];
    a[i]++;
    if(op[i] == 1) {
      G[a[i]].push_back(++cnt);
      b[i] = cnt;
    } else if(op[i] == 2) {
      cin >> b[i];
    }
  }
  dfs(1, -1);
  for(int i = 1; i <= m; i++) {
    if(op[i] == 1) {
      val[l[b[i]]] += query(l[a[i]]); // 统计多加出来的部分
    } else if(op[i] == 2) {
      update(l[a[i]], b[i]); // 树状数组的区间更新
      update(r[a[i]] + 1, -b[i]); 

    } else cout << -val[l[a[i]]] + query(l[a[i]]) << "\n"; // 当前的值减去多加的
  }
  return 0;
}