Skip to content

Commit 0280431

Browse files
authored
Merge pull request #346 from hitonanode/refactor-centroid-decomposition
Refactor centroid decomposition
2 parents 3e39c41 + ca9154c commit 0280431

File tree

3 files changed

+103
-81
lines changed

3 files changed

+103
-81
lines changed

tree/centroid_decomposition.hpp

Lines changed: 73 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,94 @@
11
#pragma once
2-
#include <tuple>
2+
#include <cassert>
33
#include <utility>
44
#include <vector>
55

6-
// CUT begin
7-
/*
8-
(Recursive) Centroid Decomposition
9-
Verification: Codeforces #190 Div.1 C https://codeforces.com/contest/321/submission/59093583
10-
11-
fix_root(int r): Build information of the tree which `r` belongs to.
12-
detect_centroid(int r): Enumerate centroid(s) of the tree which `r` belongs to.
13-
*/
6+
// Centroid Decomposition
7+
// Verification: https://yukicoder.me/problems/no/2892
8+
// find_current_centroids(int r, int conn_size): Enumerate centroid(s) of the subtree which `r` belongs to.
149
struct CentroidDecomposition {
15-
int NO_PARENT = -1;
1610
int V;
17-
int E;
18-
std::vector<std::vector<std::pair<int, int>>> to; // (node_id, edge_id)
19-
std::vector<int> par; // parent node_id par[root] = -1
20-
std::vector<std::vector<int>> chi; // children id's
21-
std::vector<int> subtree_size; // size of each subtree
22-
std::vector<int> available_edge; // If 0, ignore the corresponding edge.
23-
24-
CentroidDecomposition(int v = 0)
25-
: V(v), E(0), to(v), par(v, NO_PARENT), chi(v), subtree_size(v) {}
26-
CentroidDecomposition(const std::vector<std::vector<int>> &to_)
27-
: CentroidDecomposition(to_.size()) {
28-
for (int i = 0; i < V; i++) {
29-
for (auto j : to_[i]) {
30-
if (i < j) { add_edge(i, j); }
31-
}
11+
std::vector<std::vector<int>> to;
12+
13+
private:
14+
std::vector<int> is_alive;
15+
std::vector<int> subtree_size;
16+
17+
template <class F> void decompose(int r, int conn_size, F callback) {
18+
19+
const int c = find_current_centroids(r, conn_size).first;
20+
is_alive.at(c) = 0;
21+
22+
callback(c);
23+
24+
for (int nxt : to.at(c)) {
25+
if (!is_alive.at(nxt)) continue;
26+
int next_size = subtree_size.at(nxt);
27+
if (subtree_size.at(nxt) > subtree_size.at(c))
28+
next_size = subtree_size.at(r) - subtree_size.at(c);
29+
decompose(nxt, next_size, callback);
3230
}
3331
}
3432

33+
public:
34+
CentroidDecomposition(int v = 0) : V(v), to(v), is_alive(v, 1), subtree_size(v) {}
35+
36+
CentroidDecomposition(int v, const std::vector<std::pair<int, int>> &tree_edges)
37+
: CentroidDecomposition(v) {
38+
for (auto e : tree_edges) add_edge(e.first, e.second);
39+
}
40+
3541
void add_edge(int v1, int v2) {
36-
to[v1].emplace_back(v2, E), to[v2].emplace_back(v1, E), E++;
37-
available_edge.emplace_back(1);
42+
assert(0 <= v1 and v1 < V and 0 <= v2 and v2 < V);
43+
assert(v1 != v2);
44+
to.at(v1).push_back(v2), to.at(v2).emplace_back(v1);
3845
}
3946

40-
int _dfs_fixroot(int now, int prv) {
41-
chi[now].clear(), subtree_size[now] = 1;
42-
for (auto nxt : to[now]) {
43-
if (nxt.first != prv and available_edge[nxt.second]) {
44-
par[nxt.first] = now, chi[now].push_back(nxt.first);
45-
subtree_size[now] += _dfs_fixroot(nxt.first, now);
47+
std::pair<int, int> find_current_centroids(int r, int conn_size) {
48+
assert(is_alive.at(r));
49+
50+
const int thres = conn_size / 2;
51+
52+
int c1 = -1, c2 = -1;
53+
54+
auto rec_search = [&](auto &&self, int now, int prv) -> void {
55+
bool is_centroid = true;
56+
subtree_size.at(now) = 1;
57+
for (int nxt : to.at(now)) {
58+
if (nxt == prv or !is_alive.at(nxt)) continue;
59+
self(self, nxt, now);
60+
subtree_size.at(now) += subtree_size.at(nxt);
61+
if (subtree_size.at(nxt) > thres) is_centroid = false;
4662
}
47-
}
48-
return subtree_size[now];
49-
}
63+
if (conn_size - subtree_size.at(now) > thres) is_centroid = false;
5064

51-
void fix_root(int root) {
52-
par[root] = NO_PARENT;
53-
_dfs_fixroot(root, -1);
65+
if (is_centroid) (c1 < 0 ? c1 : c2) = now;
66+
};
67+
rec_search(rec_search, r, -1);
68+
69+
return {c1, c2};
5470
}
5571

56-
//// Centroid Decpmposition ////
57-
std::vector<int> centroid_cand_tmp;
58-
void _dfs_detect_centroids(int now, int prv, int n) {
59-
bool is_centroid = true;
60-
for (auto nxt : to[now]) {
61-
if (nxt.first != prv and available_edge[nxt.second]) {
62-
_dfs_detect_centroids(nxt.first, now, n);
63-
if (subtree_size[nxt.first] > n / 2) is_centroid = false;
72+
template <class F> void run(int r, F callback) {
73+
int conn_size = 0;
74+
75+
auto rec = [&](auto &&self, int now, int prv) -> void {
76+
++conn_size;
77+
is_alive.at(now) = 1;
78+
79+
for (int nxt : to.at(now)) {
80+
if (nxt == prv) continue;
81+
self(self, nxt, now);
6482
}
65-
}
66-
if (n - subtree_size[now] > n / 2) is_centroid = false;
67-
if (is_centroid) centroid_cand_tmp.push_back(now);
68-
}
69-
std::pair<int, int> detect_centroids(int r) { // ([centroid_node_id1], ([centroid_node_id2]|-1))
70-
centroid_cand_tmp.clear();
71-
while (par[r] != NO_PARENT) r = par[r];
72-
int n = subtree_size[r];
73-
_dfs_detect_centroids(r, -1, n);
74-
if (centroid_cand_tmp.size() == 1)
75-
return std::make_pair(centroid_cand_tmp[0], -1);
76-
else
77-
return std::make_pair(centroid_cand_tmp[0], centroid_cand_tmp[1]);
78-
}
83+
};
84+
rec(rec, r, -1);
7985

80-
std::vector<int> _cd_vertices;
81-
void _centroid_decomposition(int now) {
82-
fix_root(now);
83-
now = detect_centroids(now).first;
84-
_cd_vertices.emplace_back(now);
85-
/*
86-
do something
87-
*/
88-
for (auto p : to[now]) {
89-
int nxt, eid;
90-
std::tie(nxt, eid) = p;
91-
if (available_edge[eid] == 0) continue;
92-
available_edge[eid] = 0;
93-
_centroid_decomposition(nxt);
94-
}
86+
decompose(r, conn_size, callback);
9587
}
96-
std::vector<int> centroid_decomposition(int x) {
97-
_cd_vertices.clear();
98-
_centroid_decomposition(x);
99-
return _cd_vertices;
88+
89+
std::vector<int> centroid_decomposition(int r) {
90+
std::vector<int> res;
91+
run(r, [&](int v) { res.push_back(v); });
92+
return res;
10093
}
10194
};

tree/centroid_decomposition.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
title: Centroid decomposition (森の重心分解)
3+
documentation_of: ./centroid_decomposition.hpp
4+
---
5+
6+
与えられた森について,指定された頂点に関する連結成分の重心分解を行う.
7+
8+
## 使用方法
9+
10+
```cpp
11+
int v = 0;
12+
13+
// 頂点 v の連結成分を重心分解していく
14+
for (int c : cd.centroid_decomposition(v)) {
15+
// 頂点 c を削除する
16+
}
17+
```
18+
19+
## 問題例
20+
21+
- [No.2892 Lime and Karin - yukicoder](https://yukicoder.me/problems/no/2892)

tree/frequency_table_of_tree_distance.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,15 @@ struct frequency_table_of_tree_distance {
2222
}
2323
frequency_table_of_tree_distance(const std::vector<std::vector<int>> &to) {
2424
tos = to;
25-
cd = CentroidDecomposition(to).centroid_decomposition(0);
25+
26+
CentroidDecomposition c(to.size());
27+
for (int i = 0; i < int(to.size()); i++) {
28+
for (int j : to[i]) {
29+
if (i < j) c.add_edge(i, j);
30+
}
31+
}
32+
33+
cd = c.centroid_decomposition(0);
2634
}
2735
template <class S, std::vector<S> (*conv)(const std::vector<S> &, const std::vector<S> &)>
2836
std::vector<S> solve(const std::vector<S> &weight) {

0 commit comments

Comments
 (0)