Skip to content

Commit dd5a47f

Browse files
authored
Merge pull request #361 from hitonanode/block-cut-tree
block cut tree
2 parents 3cf69d9 + 9f13681 commit dd5a47f

File tree

4 files changed

+240
-0
lines changed

4 files changed

+240
-0
lines changed

graph/extended_block_cut_trees.hpp

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#pragma once
2+
3+
#include <cassert>
4+
#include <utility>
5+
#include <vector>
6+
7+
// Construct block cut tree (or forest) from a given graph
8+
// Complexity: O(N + M), N = |vertices|, M = |edges|
9+
// based on this idea: https://x.com/noshi91/status/1529858538650374144
10+
// based on this implementation: https://ssrs-cp.github.io/cp_library/graph/extended_block_cut_tree.hpp.html
11+
struct extended_block_cut_trees {
12+
int N; // number of vertices
13+
int B; // number of blocks
14+
std::vector<std::vector<int>> to; // (0, ..., N - 1): vertices, (N, ..., N + B - 1): blocks
15+
16+
extended_block_cut_trees(int N, const std::vector<std::pair<int, int>> &edges)
17+
: N(N), B(0), to(N) {
18+
std::vector<std::vector<int>> adj(N);
19+
for (auto [u, v] : edges) {
20+
if (u != v) adj.at(u).push_back(v), adj.at(v).push_back(u);
21+
}
22+
23+
std::vector<int> dfs_next(N, -1), dist(N, -1), back_cnt(N);
24+
25+
auto rec1 = [&](auto &&self, int now) -> void {
26+
for (int nxt : adj[now]) {
27+
if (dist[nxt] == -1) {
28+
dist[nxt] = dist[now] + 1;
29+
dfs_next[now] = nxt;
30+
self(self, nxt);
31+
back_cnt[now] += back_cnt[nxt];
32+
} else if (dist[nxt] < dist[now] - 1) {
33+
++back_cnt[now];
34+
--back_cnt[dfs_next[nxt]];
35+
}
36+
}
37+
};
38+
39+
for (int i = 0; i < N; ++i) {
40+
if (dist[i] == -1) dist[i] = 0, rec1(rec1, i);
41+
}
42+
43+
std::vector<bool> used(N);
44+
45+
auto rec2 = [&](auto &&self, int now, int current_b) -> void {
46+
used[now] = true;
47+
bool ok = false;
48+
49+
for (int nxt : adj[now]) {
50+
if (dist[nxt] == dist[now] + 1 and !used[nxt]) {
51+
if (back_cnt[nxt] > 0) {
52+
if (!ok) {
53+
ok = true;
54+
add_edge(now, current_b);
55+
}
56+
self(self, nxt, current_b);
57+
} else {
58+
to.push_back({});
59+
++B;
60+
add_edge(now, B - 1);
61+
self(self, nxt, B - 1);
62+
}
63+
}
64+
}
65+
66+
if (!ok and dist[now] > 0) { add_edge(now, current_b); }
67+
};
68+
69+
for (int i = 0; i < N; ++i) {
70+
if (dist[i] == 0) { rec2(rec2, i, B - 1); }
71+
if (adj[i].empty()) {
72+
to.push_back({});
73+
++B;
74+
add_edge(i, B - 1);
75+
}
76+
}
77+
}
78+
79+
int size() const { return N + B; }
80+
81+
bool is_articulation_point(int vertex) const {
82+
assert(0 <= vertex and vertex < N);
83+
return to[vertex].size() > 1;
84+
}
85+
86+
int block_size(int block) const {
87+
assert(0 <= block and block < B);
88+
return to[N + block].size();
89+
}
90+
91+
const std::vector<int> &block_vertices(int block) const {
92+
assert(0 <= block and block < B);
93+
return to[N + block];
94+
}
95+
96+
std::vector<std::vector<int>> biconnected_components() const {
97+
return std::vector<std::vector<int>>(to.begin() + N, to.end());
98+
}
99+
100+
// first < N (vertices), second >= N (blocks)
101+
std::vector<std::pair<int, int>> get_edges() const {
102+
std::vector<std::pair<int, int>> edges;
103+
for (int i = 0; i < N; ++i) {
104+
for (int j : to[i]) edges.emplace_back(i, j);
105+
}
106+
return edges;
107+
}
108+
109+
private:
110+
void add_edge(int vertex, int block) {
111+
assert(0 <= vertex and vertex < N);
112+
assert(0 <= block and block < B);
113+
to[vertex].push_back(N + block);
114+
to[N + block].push_back(vertex);
115+
}
116+
};

graph/extended_block_cut_trees.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
title: Extended block cut tree (Block cut tree の亜種)
3+
documentation_of: ./extended_block_cut_trees.hpp
4+
---
5+
6+
与えられた $n$ 頂点 $m$ 辺の無向グラフに対する block cut tree (のようなもの)を構築する.計算量は $O(n + m)$.
7+
8+
詳細な仕様は以下の通り.
9+
10+
- 入力グラフの二重連結成分数を $k$ とすると,頂点数 $n + k$ のグラフ(特に,森)を構築する.
11+
- **通常の block cut tree との相違点として,関節点ではない頂点も構築されたグラフに含まれる.**
12+
- 頂点番号 $0, \ldots, n - 1$ が入力グラフの各頂点, $n, \ldots, n + k - 1$ が入力グラフの各二重連結成分に対応する.
13+
- 頂点 $v$ が二重連結成分 $i$ に含まれるとき,頂点 $v$ と $n + i$ の間に辺が張られる.
14+
- このコードにおいて,次数 0 の頂点(孤立点)はそれ単独で二重連結成分と見なされる.
15+
- したがって,入力グラフの全ての頂点が一つ以上の二重連結成分に属することになる.
16+
17+
なお,出力されるグラフは以下の性質を満たす.
18+
19+
- 一般に出力されるグラフは森.特に入力グラフが連結ならば出力されるグラフも連結(木).
20+
- 入力グラフの頂点 $v$ が関節点であることは,出力されるグラフの頂点 $v$ の次数が $2$ 以上であることと同値.
21+
22+
## 使用方法
23+
24+
```cpp
25+
int n; // Num. of vertices
26+
vector<pair<int, int>> edges;
27+
28+
const extended_block_cut_trees bct(n, edges);
29+
30+
int b = bct.B; // Num. of blocks
31+
vector<vector<int>> bct_to = bct.to;
32+
assert(n + b == (int)bct_to.size());
33+
34+
// 得られた block cut tree を heavy-light decomposition などに使う場合
35+
HeavyLightDecomposition hld(bct.size());
36+
for (auto [i, j] : bct.get_edges()) hld.add_edge(i, j);
37+
```
38+
39+
## 問題例
40+
41+
- [Library Checker: Biconnected Components](https://judge.yosupo.jp/problem/biconnected_components)
42+
- [No.1326 ふたりのDominator - yukicoder](https://yukicoder.me/problems/no/1326)
43+
- 無向グラフにおいて指定された 2 頂点間の任意のパスが含む頂点の集合は, block cut tree でその 2 頂点間のパス上に存在する頂点の集合と一致する.
44+
- [AtCoder Beginner Contest 318 G - Typical Path Problem](https://atcoder.jp/contests/abc318/tasks/abc318_g)
45+
- [AtCoder Beginner Contest 334 G - Christmas Color Grid 2](https://atcoder.jp/contests/abc334/tasks/abc334_g)
46+
- [灘校文化祭コンテスト 2022 Day2 H - Tourist on Graph](https://atcoder.jp/contests/nadafes2022_day2/tasks/nadafes2022_day2_h)
47+
- [Codeforces Round 606 (Div. 1, based on Technocup 2020 Elimination Round 4) B. Two Fairs - Codeforces](https://codeforces.com/contest/1276/problem/B)
48+
- [2022-2023 Winter Petrozavodsk Camp, Day 2: GP of ainta I. Visiting Friend - Codeforces](https://codeforces.com/gym/104427/problem/I)
49+
50+
## 参考文献・リンク
51+
52+
- [Block-cut tree - ei1333の日記](https://ei1333.hateblo.jp/entry/2020/03/25/010057)
53+
- [My Algorithm : kopricky アルゴリズムライブラリ](https://kopricky.github.io/code/GraphDecomposition/articulation.html)
54+
- [拡張 Block Cut Tree | cp_library](https://ssrs-cp.github.io/cp_library/graph/extended_block_cut_tree.hpp.html)
55+
- [Xユーザーの熨斗袋さん: 「G = (V, E) に対する Block Cut Tree、G の2-点連結成分全体を C として (V ⊔ C, {{v, c} | v ∈ c}) で定義すると扱いやすかったりする?」 / X](https://x.com/noshi91/status/1529858538650374144)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/biconnected_components"
2+
#include "../extended_block_cut_trees.hpp"
3+
4+
#include <atcoder/fenwicktree>
5+
6+
#include <iostream>
7+
8+
using namespace std;
9+
10+
int main() {
11+
cin.tie(nullptr);
12+
ios::sync_with_stdio(false);
13+
14+
int N, M;
15+
cin >> N >> M;
16+
vector<pair<int, int>> edges(M);
17+
for (auto &[u, v] : edges) cin >> u >> v;
18+
19+
const extended_block_cut_trees bct(N, edges);
20+
21+
cout << bct.B << '\n';
22+
for (const auto &g : bct.biconnected_components()) {
23+
cout << g.size();
24+
for (int v : g) cout << ' ' << v;
25+
cout << '\n';
26+
}
27+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#define PROBLEM "https://yukicoder.me/problems/no/1326"
2+
#include "../extended_block_cut_trees.hpp"
3+
#include "../../tree/heavy_light_decomposition.hpp"
4+
5+
#include <atcoder/fenwicktree>
6+
7+
#include <iostream>
8+
9+
using namespace std;
10+
11+
int main() {
12+
cin.tie(nullptr);
13+
ios::sync_with_stdio(false);
14+
15+
int N, M;
16+
cin >> N >> M;
17+
vector<pair<int, int>> edges(M);
18+
for (auto &[u, v] : edges) cin >> u >> v, --u, --v;
19+
20+
const extended_block_cut_trees bct(N, edges);
21+
22+
HeavyLightDecomposition hld(bct.size());
23+
for (auto [i, j] : bct.get_edges()) hld.add_edge(i, j);
24+
hld.build();
25+
26+
atcoder::fenwick_tree<int> fw(hld.V);
27+
for (int i = 0; i < N; ++i) fw.add(hld.aligned_id[i], 1);
28+
29+
int Q;
30+
cin >> Q;
31+
while (Q--) {
32+
int u, v;
33+
cin >> u >> v;
34+
--u, --v;
35+
int ret = 0;
36+
if (u != v) {
37+
ret = -2;
38+
hld.for_each_vertex(u, v, [&](int a, int b) { ret += fw.sum(a, b + 1); });
39+
}
40+
cout << ret << '\n';
41+
}
42+
}

0 commit comments

Comments
 (0)