Skip to content

Commit

Permalink
Merge pull request #338 from hitonanode/rename-rerooting
Browse files Browse the repository at this point in the history
Rename rerooting
  • Loading branch information
hitonanode authored May 5, 2024
2 parents c596b1f + 1cfa596 commit 4ab1844
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 67 deletions.
86 changes: 46 additions & 40 deletions tree/rerooting.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,77 +8,83 @@
// Reference:
// - https://atcoder.jp/contests/abc222/editorial/2749
// - https://null-mn.hatenablog.com/entry/2020/04/14/124151
template <class Edge, class St, class Ch, Ch (*merge)(Ch, Ch), Ch (*f)(St, int, Edge),
St (*g)(Ch, int), Ch (*e)()>
template <class Edge, class Subtree, class Children, Children (*rake)(Children, Children),
Children (*add_edge)(Subtree, int, Edge), Subtree (*add_vertex)(Children, int),
Children (*e)()>
struct rerooting {
int n_;
std::vector<int> par, visited;
std::vector<std::vector<std::pair<int, Edge>>> to;
std::vector<St> dp_subtree;
std::vector<St> dp_par;
std::vector<St> dpall;

// dp_subtree[i] = DP(root=i, edge (i, par[i]) is removed).
std::vector<Subtree> dp_subtree;

// dp_par[i] = DP(root=par[i], edge (i, par[i]) is removed). dp_par[root] is meaningless.
std::vector<Subtree> dp_par;

// dpall[i] = DP(root=i, all edges exist).
std::vector<Subtree> dpall;

rerooting(const std::vector<std::vector<std::pair<int, Edge>>> &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<int> 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<Ch> left;
std::vector<Children> 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_);
Expand All @@ -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<vector<pair<int, Edge>>> to;
rerooting<Edge, Subtree, Child, merge, f, g, e> tree(to);
rerooting<Edge, Subtree, Children, rake, add_edge, add_vertex, e> tree(to);
*/
26 changes: 13 additions & 13 deletions tree/rerooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -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$ について線形時間で計算する.

## 使用方法(例)
Expand All @@ -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<vector<pair<int, tuple<>>>> to;
rerooting<tuple<>, Subtree, Child, merge, f, g, e> tree(to);
rerooting<tuple<>, Subtree, Children, rake, add_edge, add_vertex, e> tree(to);
tree.run();
for (auto x : tree.dpall) cout << x.oneway << '\n';
```
Expand Down
14 changes: 7 additions & 7 deletions tree/test/rerooting.aoj1595.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -34,7 +34,7 @@ int main() {
--u, --v;
to[u].push_back({v, {}}), to[v].push_back({u, {}});
}
rerooting<tuple<>, Subtree, Child, merge, f, g, e> tree(to);
rerooting<tuple<>, Subtree, Children, merge, add_edge, add_vertex, e> tree(to);
tree.run();
for (auto x : tree.dpall) cout << x.oneway << '\n';
}
14 changes: 7 additions & 7 deletions tree/test/rerooting.yuki1718.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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};
}
Expand All @@ -52,7 +52,7 @@ int main() {
cin >> d;
inD[d - 1] = 1;
}
rerooting<tuple<>, Subtree, Child, merge, f, g, e> tree(to);
rerooting<tuple<>, Subtree, Children, merge, add_edge, add_vertex, e> tree(to);
tree.run();
for (auto x : tree.dpall) cout << x.oneway << '\n';
}

0 comments on commit 4ab1844

Please sign in to comment.