Skip to content

Commit ec94eaa

Browse files
authored
Cuts (#64)
* saving progress * now ACs * nits * finish * start of docs * add docs * better style * nit --------- Co-authored-by: Luke Videckis <lukevideckis@gmail.com>
1 parent b7f4b47 commit ec94eaa

File tree

7 files changed

+267
-0
lines changed

7 files changed

+267
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ Cargo.lock
33
.verify-helper/
44
default_*.profraw
55
ptc_rust.profdata
6+
input

Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,14 @@ path = "examples/graphs/hld_jump_on_tree_nodes.rs"
134134
name = "hld_jump_on_tree_edges"
135135
path = "examples/graphs/hld_jump_on_tree_edges.rs"
136136

137+
[[example]]
138+
name = "cuts_yosupo"
139+
path = "examples/graphs/cuts_yosupo.rs"
140+
141+
[[example]]
142+
name = "cuts_aizu"
143+
path = "examples/graphs/cuts_aizu.rs"
144+
137145
[[example]]
138146
name = "bridges_yosupo"
139147
path = "examples/graphs/bridges_yosupo.rs"

examples/graphs/cuts_aizu.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/problems/GRL_3_A
2+
3+
use proconio::input;
4+
use programming_team_code_rust::graphs::cuts::get_cuts;
5+
6+
fn main() {
7+
input! {
8+
n: usize,
9+
m: usize,
10+
edges: [(usize, usize); m],
11+
}
12+
13+
let mut adj = vec![vec![]; n];
14+
for (i, &(u, v)) in edges.iter().enumerate() {
15+
adj[u].push((v, i));
16+
adj[v].push((u, i));
17+
}
18+
19+
let (_, is_cut, bcc_id) = get_cuts(&adj, m);
20+
21+
for i in 0..n {
22+
assert_eq!(
23+
is_cut[i],
24+
adj[i]
25+
.iter()
26+
.any(|&(_, e_id)| bcc_id[e_id] != bcc_id[adj[i][0].1])
27+
);
28+
}
29+
30+
let all_cut_nodes = is_cut
31+
.iter()
32+
.enumerate()
33+
.filter(|(_, &value)| value)
34+
.map(|(index, _)| index)
35+
.collect::<Vec<usize>>();
36+
37+
for u in all_cut_nodes {
38+
println!("{}", u);
39+
}
40+
}

examples/graphs/cuts_yosupo.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// verification-helper: PROBLEM https://judge.yosupo.jp/problem/biconnected_components
2+
3+
use proconio::input;
4+
use programming_team_code_rust::graphs::block_vertex_tree::get_bvt;
5+
use programming_team_code_rust::graphs::cuts::get_cuts;
6+
7+
fn main() {
8+
input! {
9+
n: usize,
10+
m: usize,
11+
edges: [(usize, usize); m],
12+
}
13+
14+
let mut adj = vec![vec![]; n];
15+
for (i, &(u, v)) in edges.iter().enumerate() {
16+
adj[u].push((v, i));
17+
adj[v].push((u, i));
18+
}
19+
20+
let (num_bccs, is_cut, bcc_id) = get_cuts(&adj, m);
21+
let bvt = get_bvt(&adj, num_bccs, &bcc_id);
22+
23+
for i in 0..n {
24+
assert_eq!(
25+
is_cut[i],
26+
adj[i]
27+
.iter()
28+
.any(|&(_, e_id)| bcc_id[e_id] != bcc_id[adj[i][0].1])
29+
);
30+
assert_eq!(is_cut[i], bvt[i].len() >= 2);
31+
}
32+
33+
let lone_nodes = adj
34+
.iter()
35+
.enumerate()
36+
.filter(|&(_, neighbors)| neighbors.is_empty())
37+
.map(|(i, _)| i)
38+
.collect::<Vec<usize>>();
39+
40+
println!("{}", num_bccs + lone_nodes.len());
41+
42+
for u in lone_nodes {
43+
println!("1 {}", u);
44+
}
45+
46+
for neighbors in bvt.iter().skip(n) {
47+
print!("{} ", neighbors.len());
48+
for u in neighbors {
49+
print!("{} ", u);
50+
}
51+
println!();
52+
}
53+
}

src/graphs/block_vertex_tree.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//! # Block Vertex Tree
2+
3+
/// # Guarantees
4+
/// - 0 <= bcc_id\[u\] < num_bccs
5+
///
6+
/// # Example
7+
/// ```
8+
/// use programming_team_code_rust::graphs::cuts::get_cuts;
9+
/// use programming_team_code_rust::graphs::block_vertex_tree::get_bvt;
10+
///
11+
/// let edge_list = [(0,1), (0,1), (1,2), (0,1)];
12+
/// let (n, m) = (3, 4);
13+
/// let mut adj = vec![vec![]; n];
14+
/// for (i, &(u, v)) in edge_list.iter().enumerate() {
15+
/// adj[u].push((v, i));
16+
/// adj[v].push((u, i));
17+
/// }
18+
///
19+
/// let (num_bccs, _, bcc_id) = get_cuts(&adj, m);
20+
/// let bvt = get_bvt(&adj, num_bccs, &bcc_id);
21+
///
22+
/// assert_eq!(bvt, [vec![4], vec![4, 3], vec![3], vec![1, 2], vec![0, 1]]);
23+
///
24+
/// // indexes 0..n are nodes
25+
/// // indexes n..n + num_bccs are bccs
26+
///
27+
/// for u in 0..n {
28+
/// // loop over each unique bcc containing a node u
29+
/// for bccid in bvt[u].iter().map(|v| v - n) {
30+
/// assert!(0 <= bccid && bccid < num_bccs);
31+
/// }
32+
/// }
33+
/// for bccid in 0..num_bccs {
34+
/// // loop over each unique node inside a bcc
35+
/// for &u in bvt[bccid + n].iter() {
36+
/// assert!(0 <= u && u < n);
37+
/// }
38+
/// }
39+
/// ```
40+
///
41+
/// # Complexity
42+
/// - Time: O(V + E)
43+
/// - Space: O(V)
44+
pub fn get_bvt(adj: &[Vec<(usize, usize)>], num_bccs: usize, bcc_id: &[usize]) -> Vec<Vec<usize>> {
45+
let (n, mut vis) = (adj.len(), vec![false; num_bccs]);
46+
let mut bvt = vec![vec![]; n + num_bccs];
47+
for i in 0..n {
48+
for id in adj[i].iter().map(|&(_, e_id)| bcc_id[e_id]) {
49+
if !vis[id] {
50+
vis[id] = true;
51+
bvt[i].push(id + n);
52+
bvt[id + n].push(i);
53+
}
54+
}
55+
for id in bvt[i].iter().map(|v| v - n) {
56+
vis[id] = false;
57+
}
58+
}
59+
bvt
60+
}

src/graphs/cuts.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//! # Cut nodes
2+
3+
/// # Guarantees
4+
/// - 0 <= bcc_id\[u\] < num_bccs
5+
///
6+
/// # Example
7+
/// ```
8+
/// use programming_team_code_rust::graphs::cuts::get_cuts;
9+
///
10+
/// let edge_list = [(0,1), (0,1), (1,2), (0,1)];
11+
/// let mut adj = vec![vec![]; 3];
12+
/// for (i, &(u, v)) in edge_list.iter().enumerate() {
13+
/// adj[u].push((v, i));
14+
/// adj[v].push((u, i));
15+
/// }
16+
///
17+
/// let (num_bccs, is_cut, bcc_id) = get_cuts(&adj, 4);
18+
///
19+
/// assert_eq!(num_bccs, 2);
20+
/// assert_eq!(is_cut, vec![false, true, false]);
21+
/// assert_eq!(bcc_id, vec![1, 1, 0, 1]);
22+
/// ```
23+
///
24+
/// # Panics
25+
/// ```panic
26+
/// use programming_team_code_rust::graphs::cuts::get_cuts;
27+
/// let edge_list = [(0,0)];
28+
/// let mut adj = vec![vec![]; 1];
29+
/// for (i, &(u, v)) in edge_list.iter().enumerate() {
30+
/// adj[u].push((v, i));
31+
/// adj[v].push((u, i));
32+
/// }
33+
/// let (_, _, _) = get_cuts(&adj, 1);
34+
/// ```
35+
///
36+
/// # Complexity
37+
/// - Time: O(V + E)
38+
/// - Space: O(V)
39+
pub fn get_cuts(adj: &[Vec<(usize, usize)>], m: usize) -> (usize, Vec<bool>, Vec<usize>) {
40+
#[allow(clippy::too_many_arguments)]
41+
fn dfs(
42+
u: usize,
43+
p_id: Option<usize>,
44+
adj: &[Vec<(usize, usize)>],
45+
timer: &mut usize,
46+
tin: &mut [usize],
47+
num_bccs: &mut usize,
48+
is_cut: &mut [bool],
49+
bcc_id: &mut [usize],
50+
st: &mut Vec<usize>,
51+
) -> usize {
52+
tin[u] = *timer;
53+
let (mut low, mut deg) = (*timer, 0);
54+
*timer += 1;
55+
for &(v, e_id) in &adj[u] {
56+
assert_ne!(u, v);
57+
if Some(e_id) == p_id {
58+
continue;
59+
}
60+
if tin[v] == 0 {
61+
let st_sz = st.len();
62+
st.push(e_id);
63+
let low_ch = dfs(v, Some(e_id), adj, timer, tin, num_bccs, is_cut, bcc_id, st);
64+
if low_ch >= tin[u] {
65+
is_cut[u] = true;
66+
for &id in st.iter().skip(st_sz) {
67+
bcc_id[id] = *num_bccs;
68+
}
69+
st.truncate(st_sz);
70+
*num_bccs += 1;
71+
}
72+
low = low.min(low_ch);
73+
deg += 1;
74+
} else if tin[v] < tin[u] {
75+
st.push(e_id);
76+
low = low.min(tin[v]);
77+
}
78+
}
79+
if p_id.is_none() {
80+
is_cut[u] = deg > 1;
81+
}
82+
low
83+
}
84+
let (n, mut timer, mut num_bccs, mut bcc_id, mut st) =
85+
(adj.len(), 1, 0, vec![0; m], Vec::with_capacity(m));
86+
let (mut tin, mut is_cut) = (vec![0; n], vec![false; n]);
87+
for i in 0..n {
88+
if tin[i] == 0 {
89+
dfs(
90+
i,
91+
None,
92+
adj,
93+
&mut timer,
94+
&mut tin,
95+
&mut num_bccs,
96+
&mut is_cut,
97+
&mut bcc_id,
98+
&mut st,
99+
);
100+
}
101+
}
102+
(num_bccs, is_cut, bcc_id)
103+
}

src/graphs/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
//! # Graph Algorithms
2+
pub mod block_vertex_tree;
23
pub mod bridges;
34
pub mod cent_decomp;
45
pub mod count_paths_per_length;
6+
pub mod cuts;
57
mod dfs_order;
68
pub mod dijk;
79
pub mod hld;

0 commit comments

Comments
 (0)