|
65 | 65 |
|
66 | 66 | <!-- 这里可写通用的实现逻辑 -->
|
67 | 67 |
|
| 68 | +**方法一:倍增法求 LCA** |
| 69 | + |
| 70 | +题目求的是任意两点的路径上,将其所有边的权重变成相同值的最小操作次数。实际上就是求这两点之间的路径长度,减去路径上出现次数最多的边的次数。 |
| 71 | + |
| 72 | +而求两点间的路径长度,可以通过倍增法求 LCA 来实现。我们记两点分别为 $u$ 和 $v$,最近公共祖先为 $x$,那么 $u$ 到 $v$ 的路径长度就是 $depth(u) + depth(v) - 2 \times depth(x)$。 |
| 73 | + |
| 74 | +另外,我们可以用一个数组 $cnt[n][26]$ 记录根节点到每个节点上,每个边权重出现的次数。那么 $u$ 到 $v$ 的路径上,出现次数最多的边的次数就是 $\max_{0 \leq j < 26} cnt[u][j] + cnt[v][j] - 2 \times cnt[x][j]$。其中 $x$ 为 $u$ 和 $v$ 的最近公共祖先。 |
| 75 | + |
| 76 | +倍增法求 LCA 的过程如下: |
| 77 | + |
| 78 | +我们记每个节点的深度为 $depth$,父节点为 $p$,而 $f[i][j]$ 表示节点 $i$ 的第 $2^j$ 个祖先。那么,对于任意两点 $x$ 和 $y$,我们可以通过以下方式求出它们的最近公共祖先: |
| 79 | + |
| 80 | +1. 如果 $depth(x) < depth(y)$,那么交换 $x$ 和 $y$,即保证 $x$ 的深度不小于 $y$ 的深度; |
| 81 | +2. 接下来,我们将 $x$ 的深度不断向上提升,直到 $x$ 和 $y$ 的深度相同,此时 $x$ 和 $y$ 的深度都为 $depth(x)$; |
| 82 | +3. 然后,我们将 $x$ 和 $y$ 的深度同时向上提升,直到 $x$ 和 $y$ 的父节点相同,此时 $x$ 和 $y$ 的父节点都为 $f[x][0]$,即为 $x$ 和 $y$ 的最近公共祖先。 |
| 83 | + |
| 84 | +最后,节点 $u$ 到节点 $v$ 的最小操作次数就是 $depth(u) + depth(v) - 2 \times depth(x) - \max_{0 \leq j < 26} cnt[u][j] + cnt[v][j] - 2 \times cnt[x][j]$。 |
| 85 | + |
| 86 | +时间复杂度 $O((n + q) \times C \times \log n)$,空间复杂度 $O(n \times C \times \log n)$,其中 $C$ 为边权重的最大值。 |
| 87 | + |
68 | 88 | <!-- tabs:start -->
|
69 | 89 |
|
70 | 90 | ### **Python3**
|
71 | 91 |
|
72 | 92 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
73 | 93 |
|
74 | 94 | ```python
|
75 |
| - |
| 95 | +class Solution: |
| 96 | + def minOperationsQueries( |
| 97 | + self, n: int, edges: List[List[int]], queries: List[List[int]] |
| 98 | + ) -> List[int]: |
| 99 | + m = n.bit_length() |
| 100 | + g = [[] for _ in range(n)] |
| 101 | + f = [[0] * m for _ in range(n)] |
| 102 | + p = [0] * n |
| 103 | + cnt = [None] * n |
| 104 | + depth = [0] * n |
| 105 | + for u, v, w in edges: |
| 106 | + g[u].append((v, w - 1)) |
| 107 | + g[v].append((u, w - 1)) |
| 108 | + cnt[0] = [0] * 26 |
| 109 | + q = deque([0]) |
| 110 | + while q: |
| 111 | + i = q.popleft() |
| 112 | + f[i][0] = p[i] |
| 113 | + for j in range(1, m): |
| 114 | + f[i][j] = f[f[i][j - 1]][j - 1] |
| 115 | + for j, w in g[i]: |
| 116 | + if j != p[i]: |
| 117 | + p[j] = i |
| 118 | + cnt[j] = cnt[i][:] |
| 119 | + cnt[j][w] += 1 |
| 120 | + depth[j] = depth[i] + 1 |
| 121 | + q.append(j) |
| 122 | + ans = [] |
| 123 | + for u, v in queries: |
| 124 | + x, y = u, v |
| 125 | + if depth[x] < depth[y]: |
| 126 | + x, y = y, x |
| 127 | + for j in reversed(range(m)): |
| 128 | + if depth[x] - depth[y] >= (1 << j): |
| 129 | + x = f[x][j] |
| 130 | + for j in reversed(range(m)): |
| 131 | + if f[x][j] != f[y][j]: |
| 132 | + x, y = f[x][j], f[y][j] |
| 133 | + if x != y: |
| 134 | + x = p[x] |
| 135 | + mx = max(cnt[u][j] + cnt[v][j] - 2 * cnt[x][j] for j in range(26)) |
| 136 | + ans.append(depth[u] + depth[v] - 2 * depth[x] - mx) |
| 137 | + return ans |
76 | 138 | ```
|
77 | 139 |
|
78 | 140 | ### **Java**
|
79 | 141 |
|
80 | 142 | <!-- 这里可写当前语言的特殊实现逻辑 -->
|
81 | 143 |
|
82 | 144 | ```java
|
83 |
| - |
| 145 | +class Solution { |
| 146 | + public int[] minOperationsQueries(int n, int[][] edges, int[][] queries) { |
| 147 | + int m = 32 - Integer.numberOfLeadingZeros(n); |
| 148 | + List<int[]>[] g = new List[n]; |
| 149 | + Arrays.setAll(g, i -> new ArrayList<>()); |
| 150 | + int[][] f = new int[n][m]; |
| 151 | + int[] p = new int[n]; |
| 152 | + int[][] cnt = new int[n][0]; |
| 153 | + int[] depth = new int[n]; |
| 154 | + for (var e : edges) { |
| 155 | + int u = e[0], v = e[1], w = e[2] - 1; |
| 156 | + g[u].add(new int[] {v, w}); |
| 157 | + g[v].add(new int[] {u, w}); |
| 158 | + } |
| 159 | + cnt[0] = new int[26]; |
| 160 | + Deque<Integer> q = new ArrayDeque<>(); |
| 161 | + q.offer(0); |
| 162 | + while (!q.isEmpty()) { |
| 163 | + int i = q.poll(); |
| 164 | + f[i][0] = p[i]; |
| 165 | + for (int j = 1; j < m; ++j) { |
| 166 | + f[i][j] = f[f[i][j - 1]][j - 1]; |
| 167 | + } |
| 168 | + for (var nxt : g[i]) { |
| 169 | + int j = nxt[0], w = nxt[1]; |
| 170 | + if (j != p[i]) { |
| 171 | + p[j] = i; |
| 172 | + cnt[j] = cnt[i].clone(); |
| 173 | + cnt[j][w]++; |
| 174 | + depth[j] = depth[i] + 1; |
| 175 | + q.offer(j); |
| 176 | + } |
| 177 | + } |
| 178 | + } |
| 179 | + int k = queries.length; |
| 180 | + int[] ans = new int[k]; |
| 181 | + for (int i = 0; i < k; ++i) { |
| 182 | + int u = queries[i][0], v = queries[i][1]; |
| 183 | + int x = u, y = v; |
| 184 | + if (depth[x] < depth[y]) { |
| 185 | + int t = x; |
| 186 | + x = y; |
| 187 | + y = t; |
| 188 | + } |
| 189 | + for (int j = m - 1; j >= 0; --j) { |
| 190 | + if (depth[x] - depth[y] >= (1 << j)) { |
| 191 | + x = f[x][j]; |
| 192 | + } |
| 193 | + } |
| 194 | + for (int j = m - 1; j >= 0; --j) { |
| 195 | + if (f[x][j] != f[y][j]) { |
| 196 | + x = f[x][j]; |
| 197 | + y = f[y][j]; |
| 198 | + } |
| 199 | + } |
| 200 | + if (x != y) { |
| 201 | + x = p[x]; |
| 202 | + } |
| 203 | + int mx = 0; |
| 204 | + for (int j = 0; j < 26; ++j) { |
| 205 | + mx = Math.max(mx, cnt[u][j] + cnt[v][j] - 2 * cnt[x][j]); |
| 206 | + } |
| 207 | + ans[i] = depth[u] + depth[v] - 2 * depth[x] - mx; |
| 208 | + } |
| 209 | + return ans; |
| 210 | + } |
| 211 | +} |
84 | 212 | ```
|
85 | 213 |
|
86 | 214 | ### **C++**
|
87 | 215 |
|
88 | 216 | ```cpp
|
89 |
| - |
| 217 | +class Solution { |
| 218 | +public: |
| 219 | + vector<int> minOperationsQueries(int n, vector<vector<int>>& edges, vector<vector<int>>& queries) { |
| 220 | + int m = 32 - __builtin_clz(n); |
| 221 | + vector<pair<int, int>> g[n]; |
| 222 | + int f[n][m]; |
| 223 | + int p[n]; |
| 224 | + int cnt[n][26]; |
| 225 | + int depth[n]; |
| 226 | + memset(f, 0, sizeof(f)); |
| 227 | + memset(cnt, 0, sizeof(cnt)); |
| 228 | + memset(depth, 0, sizeof(depth)); |
| 229 | + memset(p, 0, sizeof(p)); |
| 230 | + for (auto& e : edges) { |
| 231 | + int u = e[0], v = e[1], w = e[2] - 1; |
| 232 | + g[u].emplace_back(v, w); |
| 233 | + g[v].emplace_back(u, w); |
| 234 | + } |
| 235 | + queue<int> q; |
| 236 | + q.push(0); |
| 237 | + while (!q.empty()) { |
| 238 | + int i = q.front(); |
| 239 | + q.pop(); |
| 240 | + f[i][0] = p[i]; |
| 241 | + for (int j = 1; j < m; ++j) { |
| 242 | + f[i][j] = f[f[i][j - 1]][j - 1]; |
| 243 | + } |
| 244 | + for (auto& [j, w] : g[i]) { |
| 245 | + if (j != p[i]) { |
| 246 | + p[j] = i; |
| 247 | + memcpy(cnt[j], cnt[i], sizeof(cnt[i])); |
| 248 | + cnt[j][w]++; |
| 249 | + depth[j] = depth[i] + 1; |
| 250 | + q.push(j); |
| 251 | + } |
| 252 | + } |
| 253 | + } |
| 254 | + vector<int> ans; |
| 255 | + for (auto& qq : queries) { |
| 256 | + int u = qq[0], v = qq[1]; |
| 257 | + int x = u, y = v; |
| 258 | + if (depth[x] < depth[y]) { |
| 259 | + swap(x, y); |
| 260 | + } |
| 261 | + for (int j = m - 1; ~j; --j) { |
| 262 | + if (depth[x] - depth[y] >= (1 << j)) { |
| 263 | + x = f[x][j]; |
| 264 | + } |
| 265 | + } |
| 266 | + for (int j = m - 1; ~j; --j) { |
| 267 | + if (f[x][j] != f[y][j]) { |
| 268 | + x = f[x][j]; |
| 269 | + y = f[y][j]; |
| 270 | + } |
| 271 | + } |
| 272 | + if (x != y) { |
| 273 | + x = p[x]; |
| 274 | + } |
| 275 | + int mx = 0; |
| 276 | + for (int j = 0; j < 26; ++j) { |
| 277 | + mx = max(mx, cnt[u][j] + cnt[v][j] - 2 * cnt[x][j]); |
| 278 | + } |
| 279 | + ans.push_back(depth[u] + depth[v] - 2 * depth[x] - mx); |
| 280 | + } |
| 281 | + return ans; |
| 282 | + } |
| 283 | +}; |
90 | 284 | ```
|
91 | 285 |
|
92 | 286 | ### **Go**
|
93 | 287 |
|
94 | 288 | ```go
|
95 |
| - |
| 289 | +func minOperationsQueries(n int, edges [][]int, queries [][]int) []int { |
| 290 | + m := bits.Len(uint(n)) |
| 291 | + g := make([][][2]int, n) |
| 292 | + f := make([][]int, n) |
| 293 | + for i := range f { |
| 294 | + f[i] = make([]int, m) |
| 295 | + } |
| 296 | + p := make([]int, n) |
| 297 | + cnt := make([][26]int, n) |
| 298 | + cnt[0] = [26]int{} |
| 299 | + depth := make([]int, n) |
| 300 | + for _, e := range edges { |
| 301 | + u, v, w := e[0], e[1], e[2]-1 |
| 302 | + g[u] = append(g[u], [2]int{v, w}) |
| 303 | + g[v] = append(g[v], [2]int{u, w}) |
| 304 | + } |
| 305 | + q := []int{0} |
| 306 | + for len(q) > 0 { |
| 307 | + i := q[0] |
| 308 | + q = q[1:] |
| 309 | + f[i][0] = p[i] |
| 310 | + for j := 1; j < m; j++ { |
| 311 | + f[i][j] = f[f[i][j-1]][j-1] |
| 312 | + } |
| 313 | + for _, nxt := range g[i] { |
| 314 | + j, w := nxt[0], nxt[1] |
| 315 | + if j != p[i] { |
| 316 | + p[j] = i |
| 317 | + cnt[j] = [26]int{} |
| 318 | + for k := 0; k < 26; k++ { |
| 319 | + cnt[j][k] = cnt[i][k] |
| 320 | + } |
| 321 | + cnt[j][w]++ |
| 322 | + depth[j] = depth[i] + 1 |
| 323 | + q = append(q, j) |
| 324 | + } |
| 325 | + } |
| 326 | + } |
| 327 | + ans := make([]int, len(queries)) |
| 328 | + for i, qq := range queries { |
| 329 | + u, v := qq[0], qq[1] |
| 330 | + x, y := u, v |
| 331 | + if depth[x] < depth[y] { |
| 332 | + x, y = y, x |
| 333 | + } |
| 334 | + for j := m - 1; j >= 0; j-- { |
| 335 | + if depth[x]-depth[y] >= (1 << j) { |
| 336 | + x = f[x][j] |
| 337 | + } |
| 338 | + } |
| 339 | + for j := m - 1; j >= 0; j-- { |
| 340 | + if f[x][j] != f[y][j] { |
| 341 | + x, y = f[x][j], f[y][j] |
| 342 | + } |
| 343 | + } |
| 344 | + if x != y { |
| 345 | + x = p[x] |
| 346 | + } |
| 347 | + mx := 0 |
| 348 | + for j := 0; j < 26; j++ { |
| 349 | + mx = max(mx, cnt[u][j]+cnt[v][j]-2*cnt[x][j]) |
| 350 | + } |
| 351 | + ans[i] = depth[u] + depth[v] - 2*depth[x] - mx |
| 352 | + } |
| 353 | + return ans |
| 354 | +} |
| 355 | +
|
| 356 | +func max(a, b int) int { |
| 357 | + if a > b { |
| 358 | + return a |
| 359 | + } |
| 360 | + return b |
| 361 | +} |
96 | 362 | ```
|
97 | 363 |
|
98 | 364 | ### **...**
|
|
0 commit comments