diff --git a/tree/rerooting.hpp b/tree/rerooting.hpp index c2cb146c..d06d9de9 100644 --- a/tree/rerooting.hpp +++ b/tree/rerooting.hpp @@ -8,77 +8,83 @@ // Reference: // - https://atcoder.jp/contests/abc222/editorial/2749 // - https://null-mn.hatenablog.com/entry/2020/04/14/124151 -template +template struct rerooting { int n_; std::vector par, visited; std::vector>> to; - std::vector dp_subtree; - std::vector dp_par; - std::vector dpall; + + // dp_subtree[i] = DP(root=i, edge (i, par[i]) is removed). + std::vector dp_subtree; + + // dp_par[i] = DP(root=par[i], edge (i, par[i]) is removed). dp_par[root] is meaningless. + std::vector dp_par; + + // dpall[i] = DP(root=i, all edges exist). + std::vector dpall; + rerooting(const std::vector>> &to_) : n_(to_.size()), par(n_, -1), visited(n_, 0), to(to_) { - for (int i = 0; i < n_; ++i) dp_subtree.push_back(g(e(), i)); + for (int i = 0; i < n_; ++i) dp_subtree.push_back(add_vertex(e(), i)); dp_par = dpall = dp_subtree; } void run_connected(int root) { - if (visited[root]) return; - visited[root] = 1; + if (visited.at(root)) return; + visited.at(root) = 1; std::vector visorder{root}; for (int t = 0; t < int(visorder.size()); ++t) { - int now = visorder[t]; - for (const auto &edge : to[now]) { - int nxt = edge.first; - if (visited[nxt]) continue; + int now = visorder.at(t); + for (const auto &[nxt, _] : to[now]) { + if (visited.at(nxt)) continue; visorder.push_back(nxt); - visited[nxt] = 1; - par[nxt] = now; + visited.at(nxt) = 1; + par.at(nxt) = now; } } for (int t = int(visorder.size()) - 1; t >= 0; --t) { - int now = visorder[t]; - Ch ch = e(); - for (const auto &edge : to[now]) { - int nxt = edge.first; - if (nxt == par[now]) continue; - ch = merge(ch, f(dp_subtree[nxt], nxt, edge.second)); + const int now = visorder.at(t); + Children ch = e(); + for (const auto &[nxt, edge] : to.at(now)) { + if (nxt != par.at(now)) ch = rake(ch, add_edge(dp_subtree.at(nxt), nxt, edge)); } - dp_subtree[now] = g(ch, now); + dp_subtree.at(now) = add_vertex(ch, now); } - std::vector left; + std::vector left; for (int now : visorder) { - int m = int(to[now].size()); + const int m = to.at(now).size(); left.assign(m + 1, e()); for (int j = 0; j < m; j++) { - int nxt = to[now][j].first; - const St &st = (nxt == par[now] ? dp_par[now] : dp_subtree[nxt]); - left[j + 1] = merge(left[j], f(st, nxt, to[now][j].second)); + const auto &[nxt, edge] = to.at(now).at(j); + const Subtree &st = (nxt == par.at(now) ? dp_par.at(now) : dp_subtree.at(nxt)); + left.at(j + 1) = rake(left.at(j), add_edge(st, nxt, edge)); } - dpall[now] = g(left.back(), now); + dpall.at(now) = add_vertex(left.back(), now); - Ch rprod = e(); + Children rprod = e(); for (int j = m - 1; j >= 0; --j) { - int nxt = to[now][j].first; - if (nxt != par[now]) dp_par[nxt] = g(merge(left[j], rprod), now); + const auto &[nxt, edge] = to.at(now).at(j); + + if (nxt != par.at(now)) dp_par.at(nxt) = add_vertex(rake(left.at(j), rprod), now); - const St &st = (nxt == par[now] ? dp_par[now] : dp_subtree[nxt]); - rprod = merge(f(st, nxt, to[now][j].second), rprod); + const Subtree &st = (nxt == par.at(now) ? dp_par.at(now) : dp_subtree.at(nxt)); + rprod = rake(add_edge(st, nxt, edge), rprod); } } } void run() { for (int i = 0; i < n_; ++i) { - if (!visited[i]) run_connected(i); + if (!visited.at(i)) run_connected(i); } } - const St &get_subtree(int root_, int par_) const { + const Subtree &get_subtree(int root_, int par_) const { if (par_ < 0) return dpall.at(root_); if (par.at(root_) == par_) return dp_subtree.at(root_); if (par.at(par_) == root_) return dp_par.at(par_); @@ -87,13 +93,13 @@ struct rerooting { }; /* Template: struct Subtree {}; -struct Child {}; +struct Children {}; struct Edge {}; -Child e() { return Child(); } -Child merge(Child x, Child y) { return Child(); } -Child f(Subtree x, int ch_id, Edge edge) { return Child(); } -Subtree g(Child x, int v_id) { return Subtree(); } +Children e() { return Children(); } +Children rake(Children x, Children y) { return Children(); } +Children add_edge(Subtree x, int ch_id, Edge edge) { return Children(); } +Subtree add_vertex(Children x, int v_id) { return Subtree(); } vector>> to; -rerooting tree(to); +rerooting tree(to); */ diff --git a/tree/rerooting.md b/tree/rerooting.md index dbc1c70b..529162d7 100644 --- a/tree/rerooting.md +++ b/tree/rerooting.md @@ -7,12 +7,12 @@ documentation_of: ./rerooting.hpp - ここに記述する内容はリンク [1] で説明されていることとほぼ同等. - 木の各頂点・各辺になんらかのデータ構造が載っている. -- 根付き木について,各頂点 $v$ を根とする部分木に対して計算される `St` 型の情報を $X_v$ とする.また,各辺 $uv$ が持つ `Edge` 型の情報を $e_{uv}$ とする. -- $X_v$ は $X_v = g\left(\mathrm{merge}\left(f(X\_{c\_1}, c\_1, e\_{v c\_1}), \dots, f(X\_{c\_k}, c\_k, e\_{v c\_k})\right), v \right)$ を満たす.ここで $c\_1, \dots, c\_k$ は $v$ の子の頂点たち. - - $f(X\_v, v, e\_{uv})$ は $u$ の子 $v$ について `Ch` 型の情報を計算する関数. - - $\mathrm{merge}(y\_1, \dots, y\_k)$ は任意個の `Ch` 型の引数の積を計算する関数. - - $g(y, v)$ は `Ch` 型の引数 $y$ をもとに頂点 $v$ における `St` 型の情報を計算する関数. - - `Ch` 型には結合法則が成立しなければならない.また, `Ch` 型の単位元を `e()` とする. +- 根付き木について,各頂点 $v$ を根とする部分木に対して計算される `Subtree` 型の情報を $X_v$ とする.また,各辺 $uv$ が持つ `Edge` 型の情報を $e_{uv}$ とする. +- $X_v$ は $X_v = \mathrm{add\_vertex}\left(\mathrm{rake}\left(\mathrm{add\_edge}(X\_{c\_1}, c\_1, e\_{v c\_1}), \dots, \mathrm{add\_edge}(X\_{c\_k}, c\_k, e\_{v c\_k})\right), v \right)$ を満たす.ここで $c\_1, \dots, c\_k$ は $v$ の子の頂点たち. + - $\mathrm{add\_edge}(X\_v, v, e\_{uv})$ は $u$ の子 $v$ について `Children` 型の情報を計算する関数. + - $\mathrm{add\_vertex}(y, v)$ は `Children` 型の引数 $y$ をもとに頂点 $v$ における `Subtree` 型の情報を計算する関数. + - $\mathrm{rake}(y\_1, \dots, y\_k)$ は任意個の `Children` 型の引数の積を計算する関数. + - $\mathrm{rake}()$ には結合法則が成立しなければならない.また, `Children` 型の単位元を `e()` とする. - 以上のような性質を満たすデータ構造を考えたとき,本ライブラリは森の各頂点 $r$ を根とみなしたときの連結成分に関する $X_r$ の値を全ての $r$ について線形時間で計算する. ## 使用方法(例) @@ -23,29 +23,29 @@ struct Subtree { bool exist; int oneway, round; }; -struct Child { +struct Children { bool exist; int oneway, round; }; -Child merge(Child x, Child y) { +Children rake(Children x, Children y) { if (!x.exist) return y; if (!y.exist) return x; - return Child{true, min(x.oneway + y.round, y.oneway + x.round), x.round + y.round}; + return Children{true, min(x.oneway + y.round, y.oneway + x.round), x.round + y.round}; } -Child f(Subtree x, int, tuple<>) { +Children add_edge(Subtree x, int, tuple<>) { if (!x.exist) return {false, 0, 0}; return {true, x.oneway + 1, x.round + 2}; } -Subtree g(Child x, int v) { +Subtree add_vertex(Children x, int v) { if (x.exist) return {true, x.oneway, x.round}; return {inD[v], 0, 0}; return {false, 0, 0}; } -Child e() { return {false, 0, 0}; } +Children e() { return {false, 0, 0}; } vector>>> to; -rerooting, Subtree, Child, merge, f, g, e> tree(to); +rerooting, Subtree, Children, rake, add_edge, add_vertex, e> tree(to); tree.run(); for (auto x : tree.dpall) cout << x.oneway << '\n'; ``` diff --git a/tree/test/rerooting.aoj1595.test.cpp b/tree/test/rerooting.aoj1595.test.cpp index e1280e4b..6cefbb42 100644 --- a/tree/test/rerooting.aoj1595.test.cpp +++ b/tree/test/rerooting.aoj1595.test.cpp @@ -9,19 +9,19 @@ using namespace std; struct Subtree { int oneway, round; }; -struct Child { +struct Children { int oneway, round; }; -Child merge(Child x, Child y) { - return Child{min(x.oneway + y.round, y.oneway + x.round), x.round + y.round}; +Children merge(Children x, Children y) { + return Children{min(x.oneway + y.round, y.oneway + x.round), x.round + y.round}; } -Child f(Subtree x, int, tuple<>) { return {x.oneway + 1, x.round + 2}; } +Children add_edge(Subtree x, int, tuple<>) { return {x.oneway + 1, x.round + 2}; } -Subtree g(Child x, int) { return {x.oneway, x.round}; } +Subtree add_vertex(Children x, int) { return {x.oneway, x.round}; } -Child e() { return {0, 0}; } +Children e() { return {0, 0}; } int main() { cin.tie(nullptr), ios::sync_with_stdio(false); @@ -34,7 +34,7 @@ int main() { --u, --v; to[u].push_back({v, {}}), to[v].push_back({u, {}}); } - rerooting, Subtree, Child, merge, f, g, e> tree(to); + rerooting, Subtree, Children, merge, add_edge, add_vertex, e> tree(to); tree.run(); for (auto x : tree.dpall) cout << x.oneway << '\n'; } diff --git a/tree/test/rerooting.yuki1718.test.cpp b/tree/test/rerooting.yuki1718.test.cpp index 82f109d1..434580e3 100644 --- a/tree/test/rerooting.yuki1718.test.cpp +++ b/tree/test/rerooting.yuki1718.test.cpp @@ -11,24 +11,24 @@ struct Subtree { bool exist; int oneway, round; }; -struct Child { +struct Children { bool exist; int oneway, round; }; -Child e() { return {false, 0, 0}; } +Children e() { return {false, 0, 0}; } -Child merge(Child x, Child y) { +Children merge(Children x, Children y) { if (!x.exist) return y; if (!y.exist) return x; - return Child{true, min(x.oneway + y.round, y.oneway + x.round), x.round + y.round}; + return Children{true, min(x.oneway + y.round, y.oneway + x.round), x.round + y.round}; } -Child f(Subtree x, int, tuple<>) { +Children add_edge(Subtree x, int, tuple<>) { if (!x.exist) return e(); return {true, x.oneway + 1, x.round + 2}; } -Subtree g(Child x, int v) { +Subtree add_vertex(Children x, int v) { if (x.exist or inD[v]) return {true, x.oneway, x.round}; return {false, 0, 0}; } @@ -52,7 +52,7 @@ int main() { cin >> d; inD[d - 1] = 1; } - rerooting, Subtree, Child, merge, f, g, e> tree(to); + rerooting, Subtree, Children, merge, add_edge, add_vertex, e> tree(to); tree.run(); for (auto x : tree.dpall) cout << x.oneway << '\n'; }