Description:

这是一道模板题。

给一棵有根树,这棵树由编号为 1…N 的 N 个结点组成。根结点的编号为 R。每个结点都有一个权值,结点 i 的权值为 vi。
接下来有 M 组操作,操作分为两类:

1 a x,表示将结点 a 的权值增加 x;
2 a,表示求结点 a 的子树上所有结点的权值之和。

Input

第一行有三个整数 N,M 和 R。
第二行有 N 个整数,第 i 个整数表示 vi。
在接下来的 N−1 行中,每行两个整数,表示一条边。
在接下来的 M 行中,每行一组操作。

Output

对于每组 2 a 操作,输出一个整数,表示「以结点 a 为根的子树」上所有结点的权值之和。

Example

样例输入 1

10 14 9
12 -6 -4 -3 12 8 9 6 6 2
8 2
2 10
8 6
2 7
7 1
6 3
10 9
2 4
10 5
1 4 -1
2 2
1 7 -1
2 10
1 10 5
2 1
1 7 -5
2 5
1 1 8
2 7
1 8 8
2 2
1 5 5
2 6

样例输出 1

21
34
12
12
23
31
4

LibreOJ #144. DFS 序 1(dfs序+树状数组)_结点 序,它的主要思路就是将树形结构转化成线性结构,用 LibreOJ #144. DFS 序 1(dfs序+树状数组)_时间戳_02 遍历一遍这棵树,进入到 LibreOJ #144. DFS 序 1(dfs序+树状数组)_结点_03 节点有一个 LibreOJ #144. DFS 序 1(dfs序+树状数组)_权值_04 时间戳,递归退出时有一个 LibreOJ #144. DFS 序 1(dfs序+树状数组)_结点_05 时间戳,LibreOJ #144. DFS 序 1(dfs序+树状数组)_结点_03 节点的两个时间戳之间遍历到的点,就是根为 LibreOJ #144. DFS 序 1(dfs序+树状数组)_结点_03 的子树的所有节点,他们的 LibreOJ #144. DFS 序 1(dfs序+树状数组)_时间戳_02 进入时间戳是递增的。同时两个时间戳构成了一个区间,LibreOJ #144. DFS 序 1(dfs序+树状数组)_结点_03 节点在这段区间的最左端,这个区间就是一棵根节点为 LibreOJ #144. DFS 序 1(dfs序+树状数组)_结点_03

AC代码:

const int N = 1e6 + 50;
int n, q, m, r, tot, cnt;
ll val[N], in[N], out[N], head[N], x, num[N], sum[N];

struct node
{
int to, next;
} edge[2 * N];

void add(int u, int v)
{
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
}

void dfs(int u, int fa)
{
num[++cnt] = u;
in[u] = cnt;
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if (v != fa)
dfs(v, u);
}
out[u] = cnt;
}

void update(int x, int y)
{
while (x <= n)
{
sum[x] += y;
x += lowbit(x);
}
}
ll ask(int x)
{
ll res = 0;
while (x)
{
res += sum[x];
x -= lowbit(x);
}
return res;
}

int main()
{
int u, v;
cnt = 0;
sddd(n, m, r);
rep(i, 1, n)
{
sld(val[i]);
head[i] = -1;
}

rep(i, 1, n - 1)
{
sdd(u, v);
add(u, v);
add(v, u);
}
dfs(r, -1);
rep(i, 1, n)
update(in[i], val[i]);
rep(i, 1, m)
{
int op, a, x;
sd(op);
if (op == 1)
{
sdd(a, x);
update(in[a], x);
}
else
{
sd(a);
pld(ask(out[a]) - ask(in[a] - 1));
}
}
return 0;
}