33 _extendedDependsOn :
44 - icon : ' :heavy_check_mark:'
55 path : weilycoder/graph/tarjan.hpp
6- title : weilycoder/graph/tarjan.hpp
6+ title : Tarjan's Algorithm for Graph Connected Problems
77 _extendedRequiredBy : []
88 _extendedVerifiedWith : []
99 _isVerificationFailed : false
@@ -16,64 +16,88 @@ data:
1616 - https://judge.yosupo.jp/problem/biconnected_components
1717 bundledCode : " #line 1 \" test/biconnected_components.test.cpp\"\n #define PROBLEM\
1818 \ \" https://judge.yosupo.jp/problem/biconnected_components\"\n\n #line 1 \" weilycoder/graph/tarjan.hpp\" \
19- \n\n\n\n #include <algorithm>\n #include <cstddef>\n #include <stack>\n #include <utility>\n \
20- #include <vector>\n\n namespace weilycoder {\n template <typename ptr_t = size_t>\
21- \ struct StrongConnectedComponents {\n ptr_t dfs_time = 0;\n\n std::vector<bool>\
19+ \n\n\n\n /**\n * @file tarjan.hpp\n * @brief Tarjan's Algorithm for Graph Connected\
20+ \ Problems\n */\n\n #include <algorithm>\n #include <cstddef>\n #include <stack>\n \
21+ #include <utility>\n #include <vector>\n\n namespace weilycoder {\n /**\n * @brief\
22+ \ Strongly Connected Components using Tarjan's Algorithm\n * @tparam ptr_t Type\
23+ \ for representing indices (default: size_t)\n */\n template <typename ptr_t =\
24+ \ size_t> struct StrongConnectedComponents {\n ptr_t dfs_time = 0;\n\n std::vector<bool>\
2225 \ in_stack;\n std::stack<ptr_t> stk;\n std::vector<ptr_t> dfn, low;\n std::vector<std::vector<ptr_t>>\
23- \ graph;\n\n std::vector<std::vector<ptr_t>> sccs;\n\n StrongConnectedComponents(ptr_t\
24- \ n)\n : in_stack(n, false), dfn(n, 0), low(n, 0), graph(n) {}\n\n void\
25- \ add_edge(ptr_t u, ptr_t v) { graph[u].push_back(v); }\n\n void tarjan(ptr_t\
26+ \ graph;\n\n std::vector<std::vector<ptr_t>> sccs;\n\n /**\n * @brief Constructs\
27+ \ a StrongConnectedComponents for n nodes\n * @param n Number of nodes\n */\n \
28+ \ StrongConnectedComponents(ptr_t n)\n : in_stack(n, false), dfn(n, 0),\
29+ \ low(n, 0), graph(n) {}\n\n /**\n * @brief Adds a directed edge from u to\
30+ \ v\n * @param u Source node\n * @param v Destination node\n */\n void\
31+ \ add_edge(ptr_t u, ptr_t v) { graph[u].push_back(v); }\n\n /**\n * @brief\
32+ \ Tarjan's DFS to find SCCs\n * @param u Current node\n */\n void tarjan(ptr_t\
2633 \ u) {\n dfn[u] = low[u] = ++dfs_time;\n stk.push(u), in_stack[u] = true;\n \
2734 \n for (const auto &v : graph[u]) {\n if (!dfn[v])\n tarjan(v),\
2835 \ low[u] = std::min(low[u], low[v]);\n else if (in_stack[v])\n low[u]\
2936 \ = std::min(low[u], dfn[v]);\n }\n\n if (dfn[u] == low[u]) {\n sccs.emplace_back();\n \
3037 \ sccs.back().push_back(u);\n in_stack[u] = false;\n while (stk.top()\
3138 \ != u)\n sccs.back().push_back(stk.top()), in_stack[stk.top()] = false,\n \
3239 \ stk.pop();\n stk.pop();\n }\n \
33- \ }\n\n void solve() {\n for (size_t i = 0; i < graph.size(); ++i)\n \
34- \ if (!dfn[i])\n tarjan(i);\n std::reverse(sccs.begin(), sccs.end());\n \
35- \ }\n };\n\n template <typename ptr_t = size_t> struct TwoEdgeConnectedComponents\
40+ \ }\n\n /**\n * @brief Solves for all SCCs in the graph, sorting them in DFS\
41+ \ order,\n * i.e., if there is an edge from SCC A to SCC B, then A appears\
42+ \ before B\n */\n void solve() {\n for (size_t i = 0; i < graph.size();\
43+ \ ++i)\n if (!dfn[i])\n tarjan(i);\n std::reverse(sccs.begin(),\
44+ \ sccs.end());\n }\n };\n\n /**\n * @brief Two-Edge Connected Components using\
45+ \ Tarjan's Algorithm\n * @tparam ptr_t Type for representing indices (default:\
46+ \ size_t)\n */\n template <typename ptr_t = size_t> struct TwoEdgeConnectedComponents\
3647 \ {\n ptr_t dfs_time = 0, edge_time = 1;\n\n std::vector<bool> in_stack;\n \
3748 \ std::stack<ptr_t> stk;\n std::vector<ptr_t> dfn, low;\n std::vector<std::vector<std::pair<ptr_t,\
38- \ ptr_t>>> graph;\n\n std::vector<std::vector<ptr_t>> eccs;\n\n TwoEdgeConnectedComponents(ptr_t\
39- \ n)\n : in_stack(n, false), dfn(n, 0), low(n, 0), graph(n) {}\n\n void\
40- \ add_edge(ptr_t u, ptr_t v) {\n graph[u].emplace_back(v, edge_time);\n \
41- \ graph[v].emplace_back(u, edge_time);\n edge_time++;\n }\n\n void tarjan(ptr_t\
42- \ u, ptr_t parent_edge) {\n dfn[u] = low[u] = ++dfs_time;\n stk.push(u),\
43- \ in_stack[u] = true;\n\n for (const auto &[v, edge_id] : graph[u]) {\n \
44- \ if (edge_id == parent_edge)\n continue;\n if (!dfn[v])\n \
45- \ tarjan(v, edge_id), low[u] = std::min(low[u], low[v]);\n else if (in_stack[v])\n \
46- \ low[u] = std::min(low[u], dfn[v]);\n }\n\n if (dfn[u] == low[u])\
47- \ {\n eccs.emplace_back();\n eccs.back().push_back(u);\n in_stack[u]\
48- \ = false;\n while (stk.top() != u)\n eccs.back().push_back(stk.top()),\
49+ \ ptr_t>>> graph;\n\n std::vector<std::vector<ptr_t>> eccs;\n\n /**\n * @brief\
50+ \ Constructs a TwoEdgeConnectedComponents for n nodes\n * @param n Number of\
51+ \ nodes\n */\n TwoEdgeConnectedComponents(ptr_t n)\n : in_stack(n, false),\
52+ \ dfn(n, 0), low(n, 0), graph(n) {}\n\n /**\n * @brief Adds an undirected edge\
53+ \ between u and v\n * @param u One endpoint\n * @param v Other endpoint\n \
54+ \ */\n void add_edge(ptr_t u, ptr_t v) {\n graph[u].emplace_back(v, edge_time);\n \
55+ \ graph[v].emplace_back(u, edge_time);\n edge_time++;\n }\n\n /**\n \
56+ \ * @brief Tarjan's DFS to find 2-edge connected components\n * @param u Current\
57+ \ node\n * @param parent_edge Edge ID of the edge leading to u\n */\n void\
58+ \ tarjan(ptr_t u, ptr_t parent_edge) {\n dfn[u] = low[u] = ++dfs_time;\n \
59+ \ stk.push(u), in_stack[u] = true;\n\n for (const auto &[v, edge_id] : graph[u])\
60+ \ {\n if (edge_id == parent_edge)\n continue;\n if (!dfn[v])\n \
61+ \ tarjan(v, edge_id), low[u] = std::min(low[u], low[v]);\n else if\
62+ \ (in_stack[v])\n low[u] = std::min(low[u], dfn[v]);\n }\n\n if (dfn[u]\
63+ \ == low[u]) {\n eccs.emplace_back();\n eccs.back().push_back(u);\n \
64+ \ in_stack[u] = false;\n while (stk.top() != u)\n eccs.back().push_back(stk.top()),\
4965 \ in_stack[stk.top()] = false,\n stk.pop();\n \
50- \ stk.pop();\n }\n }\n\n void solve() {\n for (size_t i = 0; i <\
51- \ graph.size(); ++i)\n if (!dfn[i])\n tarjan(i, 0);\n }\n };\n\n template\
52- \ <typename ptr_t = size_t> struct BiconnectedComponents {\n ptr_t dfs_time =\
53- \ 0;\n\n std::stack<ptr_t> stk;\n std::vector<ptr_t> dfn, low;\n std::vector<std::vector<ptr_t>>\
54- \ graph;\n\n std::vector<bool> is_cut;\n std::vector<std::vector<ptr_t>> dccs;\n \
55- \n BiconnectedComponents(ptr_t n)\n : dfn(n, 0), low(n, 0), graph(n), is_cut(n,\
56- \ false) {}\n\n void add_edge(ptr_t u, ptr_t v) {\n graph[u].push_back(v);\n \
57- \ graph[v].push_back(u);\n }\n\n void tarjan(ptr_t u, bool is_root) {\n \
58- \ dfn[u] = low[u] = ++dfs_time, stk.push(u);\n\n if (is_root && graph[u].empty())\
59- \ {\n dccs.emplace_back();\n dccs.back().push_back(u);\n return;\n \
60- \ }\n\n ptr_t child = 0;\n for (const auto &v : graph[u]) {\n if\
61- \ (!dfn[v]) {\n tarjan(v, false), low[u] = std::min(low[u], low[v]);\n \
62- \ if (low[v] >= dfn[u]) {\n if (++child > 1 || !is_root)\n \
63- \ is_cut[u] = true;\n dccs.emplace_back();\n do\n \
64- \ dccs.back().push_back(stk.top()), stk.pop();\n while (dccs.back().back()\
65- \ != v);\n dccs.back().push_back(u);\n }\n } else\n \
66- \ low[u] = std::min(low[u], dfn[v]);\n }\n }\n\n void solve() {\n for\
67- \ (size_t i = 0; i < graph.size(); ++i)\n if (!dfn[i])\n tarjan(i,\
68- \ true);\n }\n };\n } // namespace weilycoder\n\n\n #line 4 \" test/biconnected_components.test.cpp\" \
69- \n #include <iostream>\n using namespace std;\n using namespace weilycoder;\n\n int\
70- \ main() {\n cin.tie(nullptr)->sync_with_stdio(false);\n cin.exceptions(cin.failbit\
71- \ | cin.badbit);\n size_t n, m;\n cin >> n >> m;\n\n BiconnectedComponents\
72- \ graph(n);\n for (size_t i = 0; i < m; ++i) {\n size_t u, v;\n cin >>\
73- \ u >> v;\n graph.add_edge(u, v);\n }\n\n graph.solve();\n\n cout << graph.dccs.size()\
74- \ << '\\ n';\n for (const auto &dcc : graph.dccs) {\n cout << dcc.size();\n \
75- \ for (size_t u : dcc)\n cout << ' ' << u;\n cout << '\\ n';\n }\n \
76- \ return 0;\n }\n "
66+ \ stk.pop();\n }\n }\n\n /**\n * @brief Solves for all 2-edge connected\
67+ \ components in the graph\n */\n void solve() {\n for (size_t i = 0; i <\
68+ \ graph.size(); ++i)\n if (!dfn[i])\n tarjan(i, 0);\n }\n };\n\n /**\n \
69+ \ * @brief Biconnected Components using Tarjan's Algorithm\n * @tparam ptr_t Type\
70+ \ for representing indices (default: size_t)\n */\n template <typename ptr_t =\
71+ \ size_t> struct BiconnectedComponents {\n ptr_t dfs_time = 0;\n\n std::stack<ptr_t>\
72+ \ stk;\n std::vector<ptr_t> dfn, low;\n std::vector<std::vector<ptr_t>> graph;\n \
73+ \n std::vector<bool> is_cut;\n std::vector<std::vector<ptr_t>> dccs;\n\n /**\n \
74+ \ * @brief Constructs a BiconnectedComponents for n nodes\n * @param n Number\
75+ \ of nodes\n */\n BiconnectedComponents(ptr_t n)\n : dfn(n, 0), low(n,\
76+ \ 0), graph(n), is_cut(n, false) {}\n\n /**\n * @brief Adds an undirected edge\
77+ \ between u and v\n * @param u One endpoint\n * @param v Other endpoint\n \
78+ \ */\n void add_edge(ptr_t u, ptr_t v) {\n graph[u].push_back(v);\n graph[v].push_back(u);\n \
79+ \ }\n\n /**\n * @brief Tarjan's DFS to find biconnected components\n * @param\
80+ \ u Current node\n * @param is_root Whether u is the root of the DFS tree\n \
81+ \ */\n void tarjan(ptr_t u, bool is_root) {\n dfn[u] = low[u] = ++dfs_time,\
82+ \ stk.push(u);\n\n if (is_root && graph[u].empty()) {\n dccs.emplace_back();\n \
83+ \ dccs.back().push_back(u);\n return;\n }\n\n ptr_t child = 0;\n \
84+ \ for (const auto &v : graph[u]) {\n if (!dfn[v]) {\n tarjan(v,\
85+ \ false), low[u] = std::min(low[u], low[v]);\n if (low[v] >= dfn[u]) {\n \
86+ \ if (++child > 1 || !is_root)\n is_cut[u] = true;\n \
87+ \ dccs.emplace_back();\n do\n dccs.back().push_back(stk.top()),\
88+ \ stk.pop();\n while (dccs.back().back() != v);\n dccs.back().push_back(u);\n \
89+ \ }\n } else\n low[u] = std::min(low[u], dfn[v]);\n }\n \
90+ \ }\n\n /**\n * @brief Solves for all biconnected components in the graph\n \
91+ \ */\n void solve() {\n for (size_t i = 0; i < graph.size(); ++i)\n \
92+ \ if (!dfn[i])\n tarjan(i, true);\n }\n };\n } // namespace weilycoder\n \
93+ \n\n #line 4 \" test/biconnected_components.test.cpp\"\n #include <iostream>\n using\
94+ \ namespace std;\n using namespace weilycoder;\n\n int main() {\n cin.tie(nullptr)->sync_with_stdio(false);\n \
95+ \ cin.exceptions(cin.failbit | cin.badbit);\n size_t n, m;\n cin >> n >> m;\n \
96+ \n BiconnectedComponents graph(n);\n for (size_t i = 0; i < m; ++i) {\n size_t\
97+ \ u, v;\n cin >> u >> v;\n graph.add_edge(u, v);\n }\n\n graph.solve();\n \
98+ \n cout << graph.dccs.size() << '\\ n';\n for (const auto &dcc : graph.dccs)\
99+ \ {\n cout << dcc.size();\n for (size_t u : dcc)\n cout << ' ' << u;\n \
100+ \ cout << '\\ n';\n }\n return 0;\n }\n "
77101 code : " #define PROBLEM \" https://judge.yosupo.jp/problem/biconnected_components\" \
78102 \n\n #include \" ../weilycoder/graph/tarjan.hpp\"\n #include <iostream>\n using namespace\
79103 \ std;\n using namespace weilycoder;\n\n int main() {\n cin.tie(nullptr)->sync_with_stdio(false);\n \
88112 isVerificationFile : true
89113 path : test/biconnected_components.test.cpp
90114 requiredBy : []
91- timestamp : ' 2025-10-29 23:28:06 +08:00'
115+ timestamp : ' 2025-10-30 21:08:13 +08:00'
92116 verificationStatus : TEST_ACCEPTED
93117 verifiedWith : []
94118documentation_of : test/biconnected_components.test.cpp
0 commit comments