#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;
}

浙公网安备 33010602011771号