[leetcode 2846][LCA]边权重均等查询

[leetcode 2846][LCA]边权重均等查询
inkOrCloud题目链接
前置概念
LCA倍增树算法
解题思路
根据LCA的的性质,要确定两个节点的最短路径只需要确定两个节点与根节点的最短路径以及两个节点最近公共祖先与根节点的最短路径即可,所以只需要初始化一个树,再预处理计算出每个节点到根节点经过的边权为x的边的数量就能得出答案
先将节点进行预处理,随机选择一个节点作为根节点,再计算出每个节点的父节点和子节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 //e[i]代表与节点i连接的节点以及边权(状态压缩)
var e = make([][]int, n)
for _, v := range edges {
i, j, w := v[0], v[1], v[2]
e[i] = append(e[i], j*27+w)
e[j] = append(e[j], i*27+w)
}
//父节点,parent[i]表示i的父节点以及边权
var parent = make([][]int, n)
var child = make([][]int, n)//子节点
var isVis = make([]bool, n)
var dep = make([]int, n)//节点深度
var treeInit func(int, int)
//从根节点开始遍历
treeInit = func(i int, d int) {
next := e[i]
dep[i] = d
for _, v := range next {
j, w := v/27, v%27
//若节点j与i相连且j未被访问则j为i的子节点
if !isVis[j] {
isVis[j] = true
parent[j] = []int{i, w}
child[i] = append(child[i], j)
treeInit(j, d+1)
}
}
}
isVis[n / 2] = true //根节点可以为任意节点
treeInit(n / 2, 0)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 var pa = make([][]int, n)
var branch = make([]int, n)
var initPa func(int)
initPa = func(i int) {
d := dep[i]
branch[d] = i
for x := 0; d-(1<<x) >= 0; x++ {
pa[i] = append(pa[i], branch[d-(1<<x)])
}
for _, v := range child[i] {
InitPa(v)
}
}
InitPa(n / 2)
var lca = func(i, j int) int {
if dep[i] > dep[j] {
h := dep[i] - dep[j]
m := bits.Len(uint(h))
for k := range m {
if (h >> k & 1) == 1 {
i = pa[i][k]
}
}
} else {
h := dep[j] - dep[i]
m := bits.Len(uint(h))
for k := range m {
if (h >> k & 1) == 1 {
j = pa[j][k]
}
}
}
if i == j {
return i
}
for x := len(pa[i]) - 1; x >= 0; x-- {
if pa[i][x] != pa[j][x] {
i, j = pa[i][x], pa[j][x]
x = len(pa[i])
}
}
return pa[i][0]
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17var weight = make([][]int, n)
for i := range weight {
//边权最大为26
weight[i] = make([]int, 27)
}
var weightInit func(int)
weightInit = func(i int) {
if parent[i] != nil {
p, w := parent[i][0], parent[i][1]
copy(weight[i], weight[p])
weight[i][w]++
}
for _, v := range child[i] {
weightInit(v)
}
}
weightInit(n / 2)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 var ret []int
for _, q := range queries {
i, j := q[0], q[1]
wi, wj := weight[i], weight[j]
wp := weight[lca(i, j)] //找出i和j的最近公共节点
sum := 0
maxS := 0
//d(i, j) = h(i) + h(j) - 2 * h(lca(i, j))
for k := 0; k <= 26; k++ {
s := wi[k] + wj[k] - 2*wp[k]
sum += s
maxS = max(maxS, s)
}
ret = append(ret, sum-maxS)
}
return ret
上述代码,树的构建时间复杂度为

![[leetcode 3321][Go][有序集合]计算子数组的 x-sum II](https://s3.inkorcloud.top/image/2025/11/c9f55b2ecb04d16a57547b9f4de294ef.png)
![[leetcode 2589][Go][贪心]完成所有任务的最少时间](https://s3.inkorcloud.top/image/2025/11/b966fd1a91aa1ea098b86e6d778523c2.png)
![[leetcode 3234][Go]统计1显著的字符串的数量](https://s3.inkorcloud.top/image/2025/11/a72a47911de291f51cbb5902e4811c1f.png)
![[leetcode 1611][Go][位运算][记忆化搜索]使整数变为 0 的最少操作次数](https://s3.inkorcloud.top/image/2025/11/9715b5f3d8a7574ded3648767538558e.png)
![[leetcode 2528][Go][二分答案][差分数组][前缀和]最大化城市的最小电量](https://s3.inkorcloud.top/image/2025/11/fa0013722d9361b4383839722394dcf2.png)