3515. 带权树中的最短路径
给你一个整数 n
和一个以节点 1 为根的无向带权树,该树包含 n
个编号从 1 到 n
的节点。它由一个长度为 n - 1
的二维数组 edges
表示,其中 edges[i] = [ui, vi, wi]
表示一条从节点 ui
到 vi
的无向边,权重为 wi
。
同时给你一个二维整数数组 queries
,长度为 q
,其中每个 queries[i]
为以下两种之一:
[1, u, v, w']
– 更新 节点 u
和 v
之间边的权重为 w'
,其中 (u, v)
保证是 edges
中存在的边。[2, x]
– 计算 从根节点 1 到节点 x
的 最短 路径距离。返回一个整数数组 answer
,其中 answer[i]
是对于第 i
个 [2, x]
查询,从节点 1 到 x
的最短路径距离。
示例 1:
输入: n = 2, edges = [[1,2,7]], queries = [[2,2],[1,1,2,4],[2,2]]
输出: [7,4]
解释:
[2,2]
:从根节点 1 到节点 2 的最短路径为 7。[1,1,2,4]
:边 (1,2)
的权重从 7 变为 4。[2,2]
:从根节点 1 到节点 2 的最短路径为 4。示例 2:
输入: n = 3, edges = [[1,2,2],[1,3,4]], queries = [[2,1],[2,3],[1,1,3,7],[2,2],[2,3]]
输出: [0,4,2,7]
解释:
[2,1]
:从根节点 1 到节点 1 的最短路径为 0。[2,3]
:从根节点 1 到节点 3 的最短路径为 4。[1,1,3,7]
:边 (1,3)
的权重从 4 改为 7。[2,2]
:从根节点 1 到节点 2 的最短路径为 2。[2,3]
:从根节点 1 到节点 3 的最短路径为 7。示例 3:
输入: n = 4, edges = [[1,2,2],[2,3,1],[3,4,5]], queries = [[2,4],[2,3],[1,2,3,3],[2,2],[2,3]]
输出: [8,3,2,5]
解释:
[2,4]
:从根节点 1 到节点 4 的最短路径包含边 (1,2)
、(2,3)
和 (3,4)
,权重和为 2 + 1 + 5 = 8
。[2,3]
:路径为 (1,2)
和 (2,3)
,权重和为 2 + 1 = 3
。[1,2,3,3]
:边 (2,3)
的权重从 1 变为 3。[2,2]
:最短路径为 2。[2,3]
:路径权重变为 2 + 3 = 5
。
提示:
1 <= n <= 105
edges.length == n - 1
edges[i] == [ui, vi, wi]
1 <= ui, vi <= n
1 <= wi <= 104
edges
构成一棵合法的树。1 <= queries.length == q <= 105
queries[i].length == 2
或 4
queries[i] == [1, u, v, w']
,或者queries[i] == [2, x]
1 <= u, v, x <= n
(u, v)
一定是 edges
中的一条边。1 <= w' <= 104
原站题解
golang 解法, 执行用时: 110 ms, 内存消耗: 47.7 MB, 提交时间: 2025-04-15 09:22:19
type fenwick []int func newFenwickTree(n int) fenwick { return make(fenwick, n+1) // 使用下标 1 到 n } // a[i] 增加 val // 1 <= i <= n func (f fenwick) update(i, val int) { for ; i < len(f); i += i & -i { f[i] += val } } // 求前缀和 a[1] + ... + a[i] // 1 <= i <= n func (f fenwick) pre(i int) (s int) { for ; i > 0; i &= i - 1 { s += f[i] } return } func treeQueries(n int, edges [][]int, queries [][]int) (ans []int) { g := make([][]int, n+1) for _, e := range edges { x, y := e[0], e[1] g[x] = append(g[x], y) g[y] = append(g[y], x) } in := make([]int, n+1) out := make([]int, n+1) clock := 0 var dfs func(int, int) dfs = func(x, fa int) { clock++ in[x] = clock // 进来的时间 for _, y := range g[x] { if y != fa { dfs(y, x) } } out[x] = clock // 离开的时间 } dfs(1, 0) // 对于一条边 x-y(y 是 x 的儿子),把边权保存在 weight[y] 中 weight := make([]int, n+1) diff := newFenwickTree(n) update := func(x, y, w int) { // 保证 y 是 x 的儿子 if in[x] > in[y] { y = x } d := w - weight[y] // 边权的增量 weight[y] = w // 把子树 y 中的最短路长度都增加 d(用差分树状数组维护) diff.update(in[y], d) diff.update(out[y]+1, -d) } for _, e := range edges { update(e[0], e[1], e[2]) } for _, q := range queries { if q[0] == 1 { update(q[1], q[2], q[3]) } else { ans = append(ans, diff.pre(in[q[1]])) } } return }
python3 解法, 执行用时: 888 ms, 内存消耗: 90.9 MB, 提交时间: 2025-04-15 09:22:05
class FenwickTree: def __init__(self, n: int): self.tree = [0] * (n + 1) # 使用下标 1 到 n # a[i] 增加 val # 1 <= i <= n def update(self, i: int, val: int) -> None: while i < len(self.tree): self.tree[i] += val i += i & -i # 计算前缀和 a[1] + ... + a[i] # 1 <= i <= n def pre(self, i: int) -> int: res = 0 while i > 0: res += self.tree[i] i &= i - 1 return res class Solution: def treeQueries(self, n: int, edges: List[List[int]], queries: List[List[int]]) -> List[int]: g = [[] for _ in range(n + 1)] for x, y, _ in edges: g[x].append(y) g[y].append(x) in_ = [0] * (n + 1) out = [0] * (n + 1) clock = 0 def dfs(x: int, fa: int) -> None: nonlocal clock clock += 1 in_[x] = clock # 进来的时间 for y in g[x]: if y != fa: dfs(y, x) out[x] = clock # 离开的时间 dfs(1, 0) weight = [0] * (n + 1) diff = FenwickTree(n) def update(x: int, y: int, w: int) -> None: # 保证 y 是 x 的儿子 if in_[x] > in_[y]: y = x d = w - weight[y] # 边权的增量 weight[y] = w # 把子树 y 中的最短路长度都增加 d(用差分树状数组维护) diff.update(in_[y], d) diff.update(out[y] + 1, -d) for e in edges: update(*e) ans = [] for q in queries: if q[0] == 1: update(*q[1:]) else: ans.append(diff.pre(in_[q[1]])) return ans
java 解法, 执行用时: 52 ms, 内存消耗: 122 MB, 提交时间: 2025-04-15 09:21:53
class FenwickTree { private final int[] tree; public FenwickTree(int n) { tree = new int[n + 1]; // 使用下标 1 到 n } // a[i] 增加 val // 1 <= i <= n public void update(int i, int val) { for (; i < tree.length; i += i & -i) { tree[i] += val; } } // 求前缀和 a[1] + ... + a[i] // 1 <= i <= n public int pre(int i) { int res = 0; for (; i > 0; i &= i - 1) { res += tree[i]; } return res; } } class Solution { public int[] treeQueries(int n, int[][] edges, int[][] queries) { List<Integer>[] g = new ArrayList[n + 1]; Arrays.setAll(g, i -> new ArrayList<>()); for (int[] e : edges) { int x = e[0]; int y = e[1]; g[x].add(y); g[y].add(x); } int[] in = new int[n + 1]; int[] out = new int[n + 1]; dfs(1, 0, g, in, out); int[] weight = new int[n + 1]; FenwickTree diff = new FenwickTree(n); for (int[] e : edges) { update(e[0], e[1], e[2], in, out, weight, diff); } List<Integer> ans = new ArrayList<>(); for (int[] q : queries) { if (q[0] == 1) { update(q[1], q[2], q[3], in, out, weight, diff); } else { ans.add(diff.pre(in[q[1]])); } } return ans.stream().mapToInt(i -> i).toArray(); } private int clock = 0; private void dfs(int x, int fa, List<Integer>[] g, int[] in, int[] out) { in[x] = ++clock; // 进来的时间 for (int y : g[x]) { if (y != fa) { dfs(y, x, g, in, out); } } out[x] = clock; // 离开的时间 } private void update(int x, int y, int w, int[] in, int[] out, int[] weight, FenwickTree diff) { // 保证 y 是 x 的儿子 if (in[x] > in[y]) { y = x; } int d = w - weight[y]; // 边权的增量 weight[y] = w; // 把子树 y 中的最短路长度都增加 d(用差分树状数组维护) diff.update(in[y], d); diff.update(out[y] + 1, -d); } }
cpp 解法, 执行用时: 171 ms, 内存消耗: 309.7 MB, 提交时间: 2025-04-15 09:21:37
template<typename T> class FenwickTree { vector<T> tree; public: // 使用下标 1 到 n FenwickTree(int n) : tree(n + 1) {} // a[i] 增加 val // 1 <= i <= n void update(int i, T val) { for (; i < tree.size(); i += i & -i) { tree[i] += val; } } // 求前缀和 a[1] + ... + a[i] // 1 <= i <= n T pre(int i) const { T res = 0; for (; i > 0; i &= i - 1) { res += tree[i]; } return res; } }; class Solution { public: vector<int> treeQueries(int n, vector<vector<int>>& edges, vector<vector<int>>& queries) { vector<vector<int>> g(n + 1); for (auto& e : edges) { int x = e[0], y = e[1]; g[x].push_back(y); g[y].push_back(x); } vector<int> in(n + 1), out(n + 1); int clock = 0; auto dfs = [&](this auto&& dfs, int x, int fa) -> void { in[x] = ++clock; // 进来的时间 for (int y : g[x]) { if (y != fa) { dfs(y, x); } } out[x] = clock; // 离开的时间 }; dfs(1, 0); // 对于一条边 x-y(y 是 x 的儿子),把边权保存在 weight[y] 中 vector<int> weight(n + 1); FenwickTree<int> diff(n); auto update = [&](int x, int y, int w) { // 保证 y 是 x 的儿子 if (in[x] > in[y]) { y = x; } int d = w - weight[y]; // 边权的增量 weight[y] = w; // 把子树 y 中的最短路长度都增加 d(用差分树状数组维护) diff.update(in[y], d); diff.update(out[y] + 1, -d); }; for (auto& e : edges) { update(e[0], e[1], e[2]); } vector<int> ans; for (auto& q : queries) { if (q[0] == 1) { update(q[1], q[2], q[3]); } else { ans.push_back(diff.pre(in[q[1]])); } } return ans; } };