LibreOJ #144. DFS 序 1(dfs序+树状数组)

    科技2025-06-21  17

    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

    D F S DFS DFS 序,它的主要思路就是将树形结构转化成线性结构,用 d f s dfs dfs 遍历一遍这棵树,进入到 x x x 节点有一个 i n in in 时间戳,递归退出时有一个 o u t out out 时间戳, x x x 节点的两个时间戳之间遍历到的点,就是根为 x x x 的子树的所有节点,他们的 d f s dfs dfs 进入时间戳是递增的。同时两个时间戳构成了一个区间, x x x 节点在这段区间的最左端,这个区间就是一棵根节点为 x x x 的子树,对于区间的操作就是其他维护方式的应用了。

    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; }
    Processed: 0.010, SQL: 8