Skip to content

Commit 4bfd32e

Browse files
committed
refactor: move LCA with binary lifting to the project
1 parent ff5d02d commit 4bfd32e

6 files changed

Lines changed: 244 additions & 296 deletions

File tree

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
#[derive(Clone)]
2+
pub struct LCA {
3+
adj: Vec<Vec<(usize, i64)>>, // the adjacency list with weights
4+
depth: Vec<i32>, // the depth/height of each vertex
5+
st: Vec<Vec<usize>>, // the sparse table for jumps
6+
weight: Vec<i64>, // the sum of weights from the root to each vertex
7+
}
8+
9+
impl LCA {
10+
/**
11+
* Create a new instance of LCA data structure
12+
* @param n the number of nodes
13+
* @return the new LCA data structure
14+
*/
15+
pub fn new(n: usize) -> Self {
16+
assert_ne!(n, 0);
17+
18+
let lg = 32 - (n as i32).leading_zeros();
19+
20+
Self {
21+
adj: vec![vec![]; n],
22+
depth: vec![0; n],
23+
st: vec![vec![0; lg as usize]; n],
24+
weight: vec![0; n],
25+
}
26+
}
27+
28+
/**
29+
* Do a DFS from the root to the vertexes to calculate some properties
30+
* @param u the vertex to do the search
31+
* @param p the parent of vertex u
32+
*/
33+
fn dfs(&mut self, u: usize, p: usize) {
34+
self.st[u][0] = p;
35+
36+
for i in 1..self.st[u].len() {
37+
self.st[u][i] = self.st[self.st[u][i - 1]][i - 1];
38+
}
39+
40+
for &(v, w) in self.adj[u].clone().iter() {
41+
if v == p {
42+
continue;
43+
}
44+
45+
self.depth[v] = 1 + self.depth[u];
46+
self.weight[v] = w + self.weight[u];
47+
self.dfs(v, u);
48+
}
49+
}
50+
51+
/**
52+
* Build the LCA data structure using a vertex as root
53+
* @param root the root of the tree
54+
*/
55+
pub fn build(&mut self, root: usize) {
56+
self.depth[root] = 0;
57+
self.weight[root] = 0;
58+
self.dfs(root, root);
59+
}
60+
61+
/**
62+
* Add an undirected edge between u and v with weight w, being that w = 1 if the tree is unweighted
63+
* @param u a vertex of the edge
64+
* @param v a vertex of the edge
65+
* @param w the weight of the edge
66+
*/
67+
pub fn add_edge(&mut self, u: usize, v: usize, w: i64) {
68+
self.adj[u].push((v, w));
69+
self.adj[v].push((u, w));
70+
}
71+
72+
/**
73+
* Find the LCA of two vertexes
74+
* @param u a vertex of pair to find the lca
75+
* @param v a vertex of pair to find the lca
76+
* @return the lca of u and v
77+
*/
78+
pub fn query(&self, mut u: usize, mut v: usize) -> usize {
79+
if self.depth[u] > self.depth[v] {
80+
std::mem::swap(&mut u, &mut v);
81+
}
82+
83+
let height = self.depth[v] - self.depth[u];
84+
85+
if height != 0 {
86+
let lg = 31 - height.leading_zeros() as usize;
87+
88+
for i in 0..=lg {
89+
if (height >> i) & 1 == 1 {
90+
v = self.st[v][i];
91+
}
92+
}
93+
}
94+
95+
if u == v {
96+
return u;
97+
}
98+
99+
let lg = 31 - self.depth[u].leading_zeros() as usize;
100+
101+
for i in (0..=lg).rev() {
102+
if self.st[u][i] == self.st[v][i] {
103+
continue;
104+
}
105+
106+
u = self.st[u][i];
107+
v = self.st[v][i];
108+
}
109+
110+
self.st[u][0]
111+
}
112+
113+
/**
114+
* Calculate the distance between two nodes
115+
* @param u the vertex of the pair to find the distance
116+
* @param v the vertex of the pair to find the distance
117+
* @return the distance between u and v
118+
*/
119+
pub fn distance(&self, u: usize, v: usize) -> i64 {
120+
let x = self.query(u, v);
121+
self.weight[u] + self.weight[v] - 2 * self.weight[x]
122+
}
123+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mod binary_lifting;
12
mod tree_diameter;
23

4+
pub use binary_lifting::LCA;
35
pub use tree_diameter::diameter;
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#[cfg(test)]
2+
mod lca_tests {
3+
use competitive_programming::graphs::LCA;
4+
5+
#[test]
6+
fn simple_path_test() {
7+
const N: usize = 10;
8+
9+
let mut lca = LCA::new(N);
10+
11+
for i in 1..N {
12+
lca.add_edge(i - 1, i, 1);
13+
}
14+
15+
lca.build(0);
16+
17+
for u in 0..N {
18+
for v in 0..N {
19+
let distance = lca.distance(u, v);
20+
let vertex = lca.query(u, v);
21+
let expected_distance = u.abs_diff(v);
22+
let expected_vertex = u.min(v);
23+
24+
assert_eq!(distance, expected_distance as i64);
25+
assert_eq!(vertex, expected_vertex);
26+
}
27+
}
28+
}
29+
30+
#[test]
31+
fn star_test() {
32+
const N: usize = 10;
33+
34+
let mut lca = LCA::new(N);
35+
36+
for i in 1..N {
37+
lca.add_edge(0, i, 1);
38+
}
39+
40+
lca.build(0);
41+
42+
for i in 1..N {
43+
let distance = lca.distance(0, i);
44+
let expected_distance = 1;
45+
46+
assert_eq!(distance, expected_distance);
47+
assert_eq!(lca.query(0, i), 0);
48+
}
49+
50+
for u in 1..N {
51+
for v in 1..N {
52+
if u == v {
53+
continue;
54+
}
55+
56+
let distance = lca.distance(u, v);
57+
let expected_distance = 2;
58+
59+
assert_eq!(distance, expected_distance);
60+
assert_eq!(lca.query(u, v), 0);
61+
}
62+
}
63+
}
64+
65+
#[test]
66+
#[allow(unused_assignments)]
67+
fn double_star_test() {
68+
const N: usize = 10;
69+
70+
let mut lca = LCA::new(N);
71+
72+
lca.add_edge(0, 1, 7);
73+
74+
for i in 2..N {
75+
lca.add_edge(i % 2, i, 7);
76+
}
77+
78+
lca.build(0);
79+
80+
for u in 0..2 {
81+
for v in 0..N {
82+
if u == v {
83+
assert_eq!(lca.distance(u, v), 0);
84+
assert_eq!(lca.query(u, v), u);
85+
} else if u == v ^ 1 {
86+
assert_eq!(lca.distance(u, v), 7);
87+
assert_eq!(lca.query(u, v), 0);
88+
} else if u % 2 == v % 2 {
89+
assert_eq!(lca.distance(u, v), 7);
90+
assert_eq!(lca.query(u, v), u);
91+
} else {
92+
assert_eq!(lca.distance(u, v), 14);
93+
assert_eq!(lca.query(u, v), 0);
94+
}
95+
}
96+
}
97+
98+
for u in 3..N {
99+
for v in (u + 1)..N {
100+
let distance = lca.distance(u, v);
101+
let vertex = lca.query(u, v);
102+
103+
let mut expected_distance = 0;
104+
let mut expected_vertex = 0;
105+
106+
if u % 2 == v % 2 {
107+
expected_distance = 14;
108+
expected_vertex = u % 2;
109+
} else {
110+
expected_distance = 21;
111+
}
112+
113+
assert_eq!(distance, expected_distance);
114+
assert_eq!(vertex, expected_vertex);
115+
}
116+
}
117+
}
118+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
pub mod binary_lifting;
12
pub mod tree_diameter;

data structures/LCA - Binary Lifting.rs

Lines changed: 0 additions & 137 deletions
This file was deleted.

0 commit comments

Comments
 (0)