Skip to content

Commit f90d418

Browse files
committed
Combine 2cc, bcc, scc in tarjan.hpp
1 parent 726b96a commit f90d418

File tree

7 files changed

+119
-182
lines changed

7 files changed

+119
-182
lines changed

cp-algo/graph/2cc.hpp

Lines changed: 0 additions & 63 deletions
This file was deleted.

cp-algo/graph/bcc.hpp

Lines changed: 0 additions & 63 deletions
This file was deleted.

cp-algo/graph/scc.hpp

Lines changed: 0 additions & 52 deletions
This file was deleted.

cp-algo/graph/tarjan.hpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#ifndef CP_ALGO_GRAPH_TARJAN_HPP
2+
#define CP_ALGO_GRAPH_TARJAN_HPP
3+
#include "base.hpp"
4+
#include "../structures/csr.hpp"
5+
#include <algorithm>
6+
#include <cassert>
7+
namespace cp_algo::graph {
8+
enum node_state { unvisited, visiting, visited, blocked };
9+
template<graph_type graph>
10+
struct tarjan_context {
11+
big_vector<int> tin, low;
12+
big_vector<node_state> state;
13+
graph const* g;
14+
int timer;
15+
structures::csr<node_index> components;
16+
tarjan_context(graph const& g):
17+
tin(g.n()), low(g.n()), state(g.n()), g(&g), timer(0) {
18+
components.reserve_data(g.n());
19+
}
20+
21+
void on_tree_edge(node_index, edge_index) {}
22+
void on_exit(node_index) {}
23+
24+
void collect(node_index v) {
25+
state[v] = blocked;
26+
components.push(v);
27+
for(auto e: g->outgoing(v)) {
28+
node_index u = g->edge(e).to;
29+
if (state[u] != blocked && state[u] != visiting) {
30+
collect(u);
31+
}
32+
}
33+
}
34+
};
35+
template<template<typename> class Context, graph_type graph>
36+
auto tarjan(graph const& g) {
37+
Context<graph> context(g);
38+
auto dfs = [&](this auto &&dfs, node_index v, edge_index ep = -1) -> void {
39+
context.state[v] = visiting;
40+
context.tin[v] = context.low[v] = context.timer++;
41+
for(auto e: g.outgoing(v)) {
42+
if constexpr (undirected_graph_type<graph>) {
43+
if (ep == graph::opposite_idx(e)) {
44+
continue;
45+
}
46+
}
47+
node_index u = g.edge(e).to;
48+
if (context.state[u] == unvisited) {
49+
dfs(u, e);
50+
context.low[v] = std::min(context.low[v], context.low[u]);
51+
context.on_tree_edge(v, u);
52+
} else if (context.state[u] != blocked) {
53+
context.low[v] = std::min(context.low[v], context.tin[u]);
54+
}
55+
}
56+
context.state[v] = visited;
57+
context.on_exit(v);
58+
};
59+
for (auto v: g.nodes()) {
60+
if (context.state[v] == unvisited) {
61+
dfs(v);
62+
}
63+
}
64+
return context.components;
65+
}
66+
template<graph_type graph>
67+
struct exit_context: tarjan_context<graph> {
68+
using base = tarjan_context<graph>;
69+
using base::base;
70+
71+
void on_exit(node_index v) {
72+
if (base::low[v] == base::tin[v]) {
73+
base::components.new_row();
74+
base::collect(v);
75+
}
76+
}
77+
};
78+
// Tarjan's algorithm for Strongly Connected Components
79+
// returns components in reverse topological order
80+
template<digraph_type graph>
81+
auto strongly_connected_components(graph const& g) {
82+
return tarjan<exit_context>(g);
83+
}
84+
85+
// Tarjan's algorithm for Two-Edge-Connected Components
86+
template<undirected_graph_type graph>
87+
auto two_edge_connected_components(graph const& g) {
88+
return tarjan<exit_context>(g);
89+
}
90+
91+
template<undirected_graph_type graph>
92+
struct bcc_context: tarjan_context<graph> {
93+
using base = tarjan_context<graph>;
94+
using base::base;
95+
void on_tree_edge(node_index v, node_index u) {
96+
if (base::low[u] >= base::tin[v]) {
97+
base::components.new_row();
98+
base::collect(u);
99+
base::components.push(v);
100+
}
101+
}
102+
void on_exit(node_index v) {
103+
if (std::empty(base::g->outgoing(v))) {
104+
base::components.new_row();
105+
base::components.push(v);
106+
}
107+
}
108+
};
109+
// Tarjan's algorithm for Biconnected Components (vertex-biconnected)
110+
template<undirected_graph_type graph>
111+
auto biconnected_components(graph const& g) {
112+
return tarjan<bcc_context>(g);
113+
}
114+
}
115+
#endif // CP_ALGO_GRAPH_TARJAN_HPP

verify/graph/2cc.test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#pragma GCC optimize("Ofast,unroll-loops")
44
#include <iostream>
55
#include "blazingio/blazingio.min.hpp"
6-
#include "cp-algo/graph/2cc.hpp"
6+
#include "cp-algo/graph/tarjan.hpp"
77
#include <bits/stdc++.h>
88

99
using namespace std;

verify/graph/bcc.test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#pragma GCC optimize("Ofast,unroll-loops")
44
#include <iostream>
55
#include "blazingio/blazingio.min.hpp"
6-
#include "cp-algo/graph/bcc.hpp"
6+
#include "cp-algo/graph/tarjan.hpp"
77
#include <bits/stdc++.h>
88

99
using namespace std;

verify/graph/scc.test.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#pragma GCC optimize("Ofast,unroll-loops")
44
#include <iostream>
55
#include "blazingio/blazingio.min.hpp"
6-
#include "cp-algo/graph/scc.hpp"
6+
#include "cp-algo/graph/tarjan.hpp"
77
#include <bits/stdc++.h>
88

99
using namespace std;
@@ -14,7 +14,7 @@ void solve() {
1414
cin >> n >> m;
1515
digraph g(n);
1616
g.read_edges(m);
17-
auto comps = scc(g);
17+
auto comps = strongly_connected_components(g);
1818
cout << size(comps) << '\n';
1919
for(auto const& comp: comps.rows() | views::reverse) {
2020
cout << size(comp) << ' ';

0 commit comments

Comments
 (0)