#144. DFS 序 1

题目描述
这是一道模板题。

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

1 a x,表示将结点 a 的权值增加 x;
2 a,表示求结点 a 的子树上所有结点的权值之和。
输入格式
第一行有三个整数 N,M 和 R。
第二行有 N 个整数,第 i 个整数表示 v_i。
在接下来的 N-1 行中,每行两个整数,表示一条边。
在接下来的 M 行中,每行一组操作。
输出格式
对于每组 \texttt{2 a} 操作,输出一个整数,表示「以结点 a 为根的子树」上所有结点的权值之和。
样例
输入复制
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
输出复制
21
34
12
12
23
31
4
数据范围与提示
1\leqslant N, M\leqslant 10^6, 1\leqslant R\leqslant N,

我们分步来做这道题

在开始之前,我们明确了题目要求我们在对应的节点有操作,所以先来建立一个查询机制BIT

点击查看代码
/ 在位置 i 上加上 x
void bit_add(int i, int x) {
    while (i < MAXN) {
        BIT[i] += x;
        i += i & -i; // lowbit(i)
    }
}

// 查询前缀和:1 到 i
int bit_sum(int i) {
    int res = 0;
    while (i > 0) {
        res += BIT[i];
        i -= i & -i; // lowbit(i)
    }
    return res;
}

然后我们要建树,根据所给数据构造这个树

点击查看代码
for(int i = 0; i <= n - 1; i++){
        int u, v;
        scanf("%d %d",&u, &v);
        add_edge(u,v);
        add_edge(v,u);
    }
    dfs(r); //根节点为r

后面就着重是处理题目要求的操作

点击查看代码
// 初始化 BIT,根据 DFS 序填入权值
    for (int i = 1; i <= n; i++){
        bit_add(in[i], value[i]); // 把每个点的权值加到它在 dfs 序上的位置
    }

    // 处理 m 个操作
    for (int i = 0; i < m; i++){
        int op;
        scanf("%d", &op);
        if (op == 1) {
            int a, x;
            scanf("%d%d", &a, &x);
            bit_add(in[a], x); // 单点加
        } else if (op == 2) {
            int a;
            scanf("%d", &a);
            int res = bit_sum(out[a]) - bit_sum(in[a] - 1); // 区间和
            printf("%d\n", res);
        }

完整代码如下

点击查看代码
#include <stdio.h>
#include <stdlib.h>

#define MAXN 100005

typedef struct Edge{
    int to;
    struct Edge* next;
} Edge;

Edge* graph[MAXN];
int value[MAXN]; //权值
int dfs_order[MAXN]; //dfs的访问顺序
int visited[MAXN];
int BIT[MAXN];

int in[MAXN], out[MAXN]; //进入时间和出时间
int time = 1,now = 1; //时间戳变量 和 当前位置

void add_edge(int u, int v){ //头插法
    Edge* e = (Edge*)malloc(sizeof(Edge));
    e->to = v;
    e->next = graph[u];  // 接到原链表头
    graph[u] = e; // 放到链表头
}

void dfs(int u){
    visited[u] = 1;
    in[u] = time++;
    dfs_order[now++] = u; // DFS序第time个节点是 u

    for(Edge* e = graph[u]; e != NULL; e = e->next){
        if(!visited[e->to]){
            dfs(e->to);
        }
    }
    out[u] = time++;
}

// 在位置 i 上加上 x
void bit_add(int i, int x) {
    while (i < MAXN) {
        BIT[i] += x;
        i += i & -i; // lowbit(i)
    }
}

// 查询前缀和:1 到 i
int bit_sum(int i) {
    int res = 0;
    while (i > 0) {
        res += BIT[i];
        i -= i & -i; // lowbit(i)
    }
    return res;
}

int main(){
    int n,m,r;
    scanf("%d%d%d",&n, &m, &r);

    // 读入权值
    for(int i = 1; i <= n; i++){
        scanf("%d", &value[i]);
    }
    //建树
    for(int i = 0; i <= n - 1; i++){
        int u, v;
        scanf("%d %d",&u, &v);
        add_edge(u,v);
        add_edge(v,u);
    }
    dfs(r); //根节点为r

    // 初始化 BIT,根据 DFS 序填入权值
    for (int i = 1; i <= n; i++){
        bit_add(in[i], value[i]); // 把每个点的权值加到它在 dfs 序上的位置
    }

    // 处理 m 个操作
    for (int i = 0; i < m; i++){
        int op;
        scanf("%d", &op);
        if (op == 1) {
            int a, x;
            scanf("%d%d", &a, &x);
            bit_add(in[a], x); // 单点加
        } else if (op == 2) {
            int a;
            scanf("%d", &a);
            int res = bit_sum(out[a]) - bit_sum(in[a] - 1); // 区间和
            printf("%d\n", res);
        }
    }
    return 0;
}
posted @ 2025-06-02 01:13  sirro1uta  阅读(14)  评论(0)    收藏  举报