Skip to content

Commit df1e053

Browse files
authored
Create 2846-minimum-edge-weight-equilibrium-queries-in-a-tree.js
1 parent 57bf73c commit df1e053

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/**
2+
* @param {number} n
3+
* @param {number[][]} edges
4+
* @param {number[][]} queries
5+
* @return {number[]}
6+
*/
7+
const minOperationsQueries = function (n, edges, queries) {
8+
let [directParents, counts, depths] = getParentsAndPrefixCounts(n, edges)
9+
let lcaModule = new LCA(n, directParents, depths)
10+
let ans = []
11+
for (let [a, b] of queries) {
12+
let lca = lcaModule.getLCA(a, b)
13+
let countsA = diffCounts(counts[a], counts[lca]),
14+
countsB = diffCounts(counts[b], counts[lca])
15+
let totalCounts = addCounts(countsA, countsB)
16+
let edgesInPath = depths[a] - depths[lca] + depths[b] - depths[lca]
17+
let maxCount = 0
18+
for (let i = 1; i <= 26; i++) {
19+
maxCount = Math.max(maxCount, totalCounts[i])
20+
}
21+
ans.push(edgesInPath - maxCount) // turn all other non-majority weights into the weight with the most occurances
22+
}
23+
return ans
24+
}
25+
26+
function addCounts(countsA, countsB) {
27+
let total = Array(27)
28+
for (let i = 1; i <= 26; i++) {
29+
total[i] = countsA[i] + countsB[i]
30+
}
31+
return total
32+
}
33+
34+
function diffCounts(countsA, countsLCA) {
35+
let diff = Array(27)
36+
for (let i = 1; i <= 26; i++) {
37+
diff[i] = countsA[i] - countsLCA[i]
38+
}
39+
return diff
40+
}
41+
42+
function getParentsAndPrefixCounts(n, edges) {
43+
let directParents = Array(n).fill(-1)
44+
let graph = Array(n)
45+
.fill(0)
46+
.map(() => [])
47+
let prefixCounts = Array(n)
48+
for (let [u, v, w] of edges) {
49+
graph[u].push([v, w])
50+
graph[v].push([u, w])
51+
}
52+
let seen = Array(n).fill(false)
53+
seen[0] = true
54+
let queue = [[0, Array(27).fill(0), 0]]
55+
let depths = Array(n)
56+
while (queue.length) {
57+
let [node, count, depth] = queue.shift()
58+
prefixCounts[node] = count
59+
depths[node] = depth
60+
61+
for (let [nei, weight] of graph[node]) {
62+
if (seen[nei]) continue
63+
let newCount = [...count]
64+
newCount[weight]++
65+
seen[nei] = true
66+
queue.push([nei, newCount, depth + 1])
67+
directParents[nei] = node
68+
}
69+
}
70+
return [directParents, prefixCounts, depths]
71+
}
72+
73+
class LCA {
74+
constructor(n, directParents, depths) {
75+
this.maxDepth = Math.ceil(Math.log2(n))
76+
this.p = Array(this.maxDepth + 1)
77+
.fill(0)
78+
.map(() => Array(n).fill(-1))
79+
this.depths = depths
80+
81+
// precomputation for binary lifting
82+
for (let node = 0; node < n; node++) {
83+
this.p[0][node] = directParents[node]
84+
}
85+
for (let pow2 = 1; pow2 <= this.maxDepth; pow2++) {
86+
for (let node = 0; node < n; node++) {
87+
let halfParent = this.p[pow2 - 1][node]
88+
this.p[pow2][node] =
89+
halfParent === -1 ? -1 : this.p[pow2 - 1][halfParent]
90+
}
91+
}
92+
}
93+
getLCA(a, b) {
94+
if (this.depths[a] > this.depths[b]) {
95+
let temp = a
96+
a = b
97+
b = temp
98+
}
99+
100+
// bring both nodes up to the same depth
101+
let depthDiff = this.depths[b] - this.depths[a]
102+
for (let i = 0; i <= this.maxDepth; i++) {
103+
if ((depthDiff >> i) & 1) {
104+
b = this.p[i][b] // move b up to the 2^ith parent
105+
}
106+
}
107+
if (a === b) return a
108+
109+
// move both nodes up by 2^ith levels if the 2^ith parents are not equal
110+
for (let i = this.maxDepth; i >= 0; i--) {
111+
// this decrements so that we can jump the nodes up incrementally
112+
if (this.p[i][a] !== this.p[i][b]) {
113+
// if 2^ith parents of both nodes are not equal, we can safely both move up
114+
a = this.p[i][a]
115+
b = this.p[i][b]
116+
}
117+
}
118+
return this.p[0][a]
119+
}
120+
}

0 commit comments

Comments
 (0)