Skip to main content

nekolib/graph/
scc_.rs

1//! 強連結成分分解。
2
3/// lowlink に基づく強連結成分分解。
4///
5/// # Parameters
6/// - `n`: 頂点数
7/// - `vs`: 頂点集合
8/// - `index`: 頂点から添字への番号づけをする関数
9/// - `delta`: 頂点 `v` と関数 `search` を受け取る関数
10///
11/// `delta` は、`v` の各隣接頂点 `nv` に対して、`search(nv)`
12/// を呼び出す必要がある。
13///
14/// # Return value
15/// `index(v)` 番目の要素が `v` の属する強連結成分の番号である配列。
16/// 番号づけはトポロジカル順に行われる。
17///
18/// # Examples
19///
20/// ```text
21/// (0) ---> (1) ---> (3) ---> (5) ---> (6) ---> (7)
22///  ^        |        |        ^        ^        |
23///  |        v        v        |        |        |
24/// (4) <--- (2)      (9)       +------ (8) <-----+
25/// ```
26///
27/// ```
28/// use nekolib::graph::scc;
29///
30/// let g = vec![
31///     vec![1],
32///     vec![2, 3],
33///     vec![4],
34///     vec![5, 9],
35///     vec![0],
36///     vec![6],
37///     vec![7],
38///     vec![8],
39///     vec![5, 6],
40///     vec![],
41/// ];
42/// let index = |&v: &usize| v;
43/// let delta = |&v: &usize| g[v].iter().cloned();
44/// let comp_id = scc(10, 0..10, index, delta);
45///
46/// assert_eq!(comp_id, vec![0, 0, 0, 1, 0, 3, 3, 3, 3, 2]);
47/// ```
48///
49/// # Complexity
50/// $O(|V|+|E|)$ 時間。
51///
52/// # References
53/// - <https://niuez.github.io/posts/impl_abstract_dijkstra/>
54pub fn scc<V, E>(
55    n: usize,
56    vs: impl Iterator<Item = V>,
57    index: impl Fn(&V) -> usize + Copy,
58    delta: impl Fn(&V) -> E + Copy,
59) -> Vec<usize>
60where
61    E: Iterator<Item = V>,
62{
63    struct State {
64        scc: Vec<Vec<usize>>,
65        num: Vec<usize>,
66        low: Vec<usize>,
67        s: Vec<usize>,
68        ins: Vec<bool>,
69        t: usize,
70    }
71
72    fn dfs<V, E>(
73        v: V,
74        index: impl Fn(&V) -> usize + Copy,
75        delta: impl Fn(&V) -> E + Copy,
76        state: &mut State,
77    ) where
78        E: Iterator<Item = V>,
79    {
80        state.t += 1;
81        let vi = index(&v);
82        state.low[vi] = state.t;
83        state.num[vi] = state.t;
84        state.s.push(vi);
85        state.ins[vi] = true;
86        for nv in delta(&v) {
87            let nvi = index(&nv);
88            if state.num[nvi] == 0 {
89                dfs(nv, index, delta, state);
90                state.low[vi] = state.low[vi].min(state.low[nvi]);
91            } else if state.ins[nvi] {
92                state.low[vi] = state.low[vi].min(state.num[nvi]);
93            }
94        }
95        if state.low[vi] == state.num[vi] {
96            let mut tmp = vec![];
97            loop {
98                let nvi = state.s.pop().unwrap();
99                state.ins[nvi] = false;
100                tmp.push(nvi);
101                if vi == nvi {
102                    break;
103                }
104            }
105            state.scc.push(tmp);
106        }
107    }
108
109    let mut state = State {
110        scc: vec![],
111        num: vec![0; n],
112        low: vec![0; n],
113        s: vec![],
114        ins: vec![false; n],
115        t: 0,
116    };
117
118    for v in vs {
119        if state.num[index(&v)] == 0 {
120            dfs(v, index, delta, &mut state);
121        }
122    }
123
124    let mut res = vec![0; n];
125    for i in 0..state.scc.len() {
126        for &c in &state.scc[i] {
127            res[c] = state.scc.len() - i - 1;
128        }
129    }
130
131    res
132}