写代码c语言已知有n个节点,有n-1条边,形成一个树的结构. 给定一个根节点S.每个节点都有一个权值,节点i的权值为vi. 给出m个操作.操作有两种类型: 1 a x ,表示将节点a的权值加上x 2 a ,表示求a节点的子树上所有节点的和(包括a节点本身)
时间: 2024-04-26 09:20:27 浏览: 110
树结构c语言代码
3星 · 编辑精心推荐
下面是使用C语言实现的代码,基于树的遍历实现:
```c
#include <stdio.h>
#include <stdlib.h>
#define MAXN 100005
typedef long long ll;
int n, m; // 节点数和操作数
int v[MAXN]; // 节点权值
int head[MAXN], nxt[MAXN * 2], to[MAXN * 2], cnt = 0; // 邻接表存储图
int fa[MAXN], sz[MAXN], son[MAXN], top[MAXN], dep[MAXN], id[MAXN], rk[MAXN], tot = 0; // 树剖相关数组
ll sum[MAXN << 2], add[MAXN << 2]; // 线段树相关数组
inline void add_edge(int u, int v) {
nxt[++cnt] = head[u];
head[u] = cnt;
to[cnt] = v;
}
void dfs1(int u, int f) {
fa[u] = f;
sz[u] = 1;
dep[u] = dep[f] + 1;
for (int i = head[u]; i; i = nxt[i]) {
int v = to[i];
if (v == f) continue;
dfs1(v, u);
sz[u] += sz[v];
if (sz[v] > sz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int t) {
top[u] = t;
id[u] = ++tot;
rk[tot] = u;
if (!son[u]) return;
dfs2(son[u], t);
for (int i = head[u]; i; i = nxt[i]) {
int v = to[i];
if (v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
inline void pushup(int i) {
sum[i] = sum[i << 1] + sum[i << 1 | 1];
}
inline void pushdown(int i, int l, int r) {
if (add[i] != 0) {
int mid = (l + r) >> 1;
add[i << 1] += add[i];
sum[i << 1] += (mid - l + 1) * add[i];
add[i << 1 | 1] += add[i];
sum[i << 1 | 1] += (r - mid) * add[i];
add[i] = 0;
}
}
void build(int i, int l, int r) {
if (l == r) {
sum[i] = v[rk[l]];
return;
}
int mid = (l + r) >> 1;
build(i << 1, l, mid);
build(i << 1 | 1, mid + 1, r);
pushup(i);
}
void update(int i, int l, int r, int ql, int qr, int x) {
if (ql <= l && r <= qr) {
add[i] += x;
sum[i] += (r - l + 1) * x;
return;
}
pushdown(i, l, r);
int mid = (l + r) >> 1;
if (ql <= mid) update(i << 1, l, mid, ql, qr, x);
if (qr > mid) update(i << 1 | 1, mid + 1, r, ql, qr, x);
pushup(i);
}
ll query(int i, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) return sum[i];
pushdown(i, l, r);
int mid = (l + r) >> 1;
ll ans = 0;
if (ql <= mid) ans += query(i << 1, l, mid, ql, qr);
if (qr > mid) ans += query(i << 1 | 1, mid + 1, r, ql, qr);
return ans;
}
void modify(int x, int y) {
update(1, 1, n, id[x], id[x], y);
}
ll query_subtree(int x) {
ll ans = 0;
while (top[x] != top[1]) {
ans += query(1, 1, n, id[top[x]], id[x]);
x = fa[top[x]];
}
ans += query(1, 1, n, 1, id[x]);
return ans;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &v[i]);
for (int i = 1; i < n; i++) {
int u, v;
scanf("%d %d", &u, &v);
add_edge(u, v);
add_edge(v, u);
}
dfs1(1, 0);
dfs2(1, 1);
build(1, 1, n);
while (m--) {
int op, a, x;
scanf("%d %d", &op, &a);
if (op == 1) {
scanf("%d", &x);
modify(a, x);
} else {
printf("%lld\n", query_subtree(a));
}
}
return 0;
}
```
代码中使用了树剖和线段树两种数据结构,其中树剖用于快速求子树和,线段树用于维护节点权值的修改。在每次操作时,如果是修改操作,则直接在对应节点上加上给定的值,如果是查询操作,则使用树剖求出对应节点的子树范围(从该节点到其子树的最深叶子节点),然后在线段树上查询该范围内所有节点权值之和即可。
阅读全文