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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
use std::collections::HashMap;
use lowest_common_ancestor::LowestCommonAncestor;
/// [auxiliary tree](https://noshi91.github.io/algorithm-encyclopedia/auxiliary-tree) です。
///
/// # 計算量
///
/// hash map のコストは無視する。
///
/// グラフの頂点数を n, `nodes.len()` を k として、O(klogn + klogk)
///
/// # 引数
///
/// * `nodes`: {0, 1, ..., n - 1} の部分集合
/// * `inv_ord`: pre-order (行きがけ順) の列の添字と値を反対にしたもの
/// * 頂点 `i` は行きがけ順で `inv_ord[i]` 番目に現われる
/// * `lca`: 2頂点の LCA を得る `.get(u, v)` を実装した構造体
///
/// # 返り値
///
/// `(root, g)`
///
/// * ` g.contains_key(&i)`: 頂点 `i` が圧縮後の木に含まれて、子のリストが `g[&i]` である
/// * `!g.contains_key(&i)`: 頂点 `i` が圧縮後の木に含まれない
pub fn auxiliary_tree(
nodes: &[usize],
inv_ord: &[usize],
lca: &LowestCommonAncestor, // trait にする?
) -> (usize, HashMap<usize, Vec<usize>>) {
// https://smijake3.hatenablog.com/entry/2019/09/15/200200
assert!(!nodes.is_empty());
// nodes.len() < 2 だと .windows(2) が空になるので場合分け
if nodes.len() == 1 {
return (nodes[0], HashMap::from([(nodes[0], vec![])]));
}
let mut nodes = nodes.to_vec();
nodes.sort_by_key(|&i| inv_ord[i]);
let lca_nodes = nodes
.windows(2)
.map(|w| lca.get(w[0], w[1]))
.collect::<Vec<_>>();
nodes.extend(lca_nodes);
nodes.sort_by_key(|&i| inv_ord[i]);
nodes.dedup();
let mut h = HashMap::<_, Vec<_>>::new();
for w in nodes.windows(2) {
// stack 使わずにこれでよさそう
let x = lca.get(w[0], w[1]);
h.entry(x).or_insert_with(Vec::new).push(w[1]);
assert!(!h.contains_key(&w[1]));
h.insert(w[1], vec![]);
}
(nodes[0], h)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_line() {
// * *
// 0 (root) -- 1 -- 2 -- 3 -- 4
assert_eq!(
auxiliary_tree(
&[2, 4],
&[0, 1, 2, 3, 4],
&LowestCommonAncestor::new(5, 0, &[(0, 1), (1, 2), (2, 3), (3, 4)])
),
(2, HashMap::from([(2, vec![4]), (4, vec![])]))
);
}
}