Solution

比较经典的题型了吧, 我的做法是dfs序 + 树状数组
因为题目要求我们做到单点修改, 区间求和的一个操作
显然树状数组能够满足要求
那么问题便转化为如何把树上问题转化为简单的区间问题
考虑到每一个子树在dfs序下一定是连续的
图片说明
我们可以保存它的编号最小点和编号最大点, 用来表示它的子树
比如, 对于 2 这个节点, 我们保存 2 和 4 表示以 2 为根这棵子树
剩下的就是很模板的树状数组区间求和, 单点更新了
具体看下代码吧

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long long LL;
const int N = 1e6 + 5;
const ll mod = 998244353;
const int DEG = 30;
const double PI = acos(-1.0);
const double eps = 1e-12;
const ll inf = 1e18;
static auto faster_iostream = []() {
    std::ios::sync_with_stdio(false);  // turn off sync
    std::cin.tie(nullptr);             // untie in/out streams
    return 0;
}();
int T[N];
int lowbit(int x){
  return x & (-x);
}
void update(int x, int add){
  while(x < N){
    T[x] += add;
    x += lowbit(x);
  }
}
ll query(int x){
  ll res = 0;
  while(x){
    res += T[x];
    x -= lowbit(x);
  }
  return res;
}
struct Edge{
  int v, nextz;
}edge[N << 1];
int tot, head[N];
void init(){
  memset(head, -1, sizeof(head));
}
void addedge(int u, int v){
  edge[tot].v = v;
  edge[tot].nextz = head[u];
  head[u] = tot++;
}
int l[N], r[N];
int cnt = 0;
int a[N];
void dfs(int u, int par){
  l[u] = ++cnt;
  for(int i = head[u]; ~i; i = edge[i].nextz){
    int v = edge[i].v;
    if(v == par) continue;
    dfs(v, u);
  }
  r[u] = cnt;
}
int main(){
  init();
  int n, m, root;
  cin >> n >> m >> root;
  for(int i = 1; i <= n; i++) cin >> a[i];
  for(int i = 1; i <= n - 1; i++){
    int u, v;
    cin >> u >> v;
    addedge(u, v);
    addedge(v, u);
  }
  dfs(root, 0);
  for(int i = 1; i <= n; i++) update(l[i], a[i]);
  while(m--){
    int op;
    cin >> op;
    int x, y;
    if(op == 1){
      cin >> x >> y;
      update(l[x], y);
    }
    else {
      cin >> x;
      cout << query(r[x]) - query(l[x] - 1) << "\n";
    }
  }
  return 0;
}