Skip to content

Commit 083f3f8

Browse files
committed
Add scc
1 parent a79cc1d commit 083f3f8

File tree

3 files changed

+148
-0
lines changed

3 files changed

+148
-0
lines changed

cp-algo/graph/scc.hpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#ifndef CP_ALGO_GRAPH_SCC_HPP
2+
#define CP_ALGO_GRAPH_SCC_HPP
3+
#include "base.hpp"
4+
#include "../structures/csr.hpp"
5+
#include <algorithm>
6+
namespace cp_algo::graph {
7+
// Tarjan's algorithm for Strongly Connected Components
8+
// returns components in reverse topological order
9+
template<digraph_type graph>
10+
structures::csr<node_index> scc(graph const& g) {
11+
structures::csr<node_index> components;
12+
components.reserve_data(g.n());
13+
enum node_state { unvisited, visited, collected };
14+
std::vector<node_state> state(g.n());
15+
auto collect = [&](this auto &&collect, node_index v) -> void {
16+
state[v] = collected;
17+
components.push(v);
18+
for(auto e: g.outgoing(v)) {
19+
node_index u = g.edge(e).to;
20+
if (state[u] != collected) {
21+
collect(u);
22+
}
23+
}
24+
};
25+
big_vector<int> tin(g.n()), low(g.n());
26+
int timer = 0;
27+
auto dfs = [&](this auto &&dfs, node_index v) -> void {
28+
state[v] = visited;
29+
tin[v] = low[v] = timer++;
30+
for(auto e: g.outgoing(v)) {
31+
node_index u = g.edge(e).to;
32+
if (state[u] == unvisited) {
33+
dfs(u);
34+
low[v] = std::min(low[v], low[u]);
35+
} else if (state[u] == visited) {
36+
low[v] = std::min(low[v], tin[u]);
37+
}
38+
}
39+
if (low[v] == tin[v]) {
40+
components.new_row();
41+
collect(v);
42+
}
43+
};
44+
for (auto v: g.nodes()) {
45+
if (state[v] == unvisited) {
46+
dfs(v);
47+
}
48+
}
49+
return components;
50+
}
51+
}
52+
#endif // CP_ALGO_GRAPH_SCC_HPP

cp-algo/structures/csr.hpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#ifndef CP_ALGO_STRUCTURES_CSR_HPP
2+
#define CP_ALGO_STRUCTURES_CSR_HPP
3+
#include "../util/big_alloc.hpp"
4+
#include <algorithm>
5+
#include <numeric>
6+
#include <ranges>
7+
#include <span>
8+
namespace cp_algo::structures {
9+
template<class datatype>
10+
struct csr {
11+
csr(): head{0} { }
12+
13+
void new_row() {
14+
head.push_back(head.back());
15+
}
16+
17+
void push(datatype const& value) {
18+
data.push_back(value);
19+
head.back()++;
20+
}
21+
22+
template<typename... Args>
23+
void emplace(Args&&... args) {
24+
data.emplace_back(std::forward<Args>(args)...);
25+
head.back()++;
26+
}
27+
28+
void reserve_rows(int n) {
29+
head.reserve(n + 1);
30+
}
31+
32+
void reserve_data(size_t n) {
33+
data.reserve(n);
34+
}
35+
36+
void reverse_rows() {
37+
std::ranges::reverse(data);
38+
std::adjacent_difference(head.begin(), head.end(), head.begin());
39+
std::ranges::reverse(head | std::views::drop(1));
40+
std::partial_sum(head.begin(), head.end(), head.begin());
41+
}
42+
43+
size_t size() const { return head.size() - 1; }
44+
size_t total_size() const { return data.size(); }
45+
46+
auto operator[](this auto&& self, auto row) {
47+
return std::span(self.data).subspan(self.head[row], self.head[row + 1] - self.head[row]);
48+
}
49+
50+
auto rows(this auto&& self) {
51+
return std::views::iota(size_t(0), self.size())
52+
| std::views::transform([&self](auto i) { return self[i]; });
53+
}
54+
55+
big_vector<int> head;
56+
big_vector<datatype> data;
57+
};
58+
}
59+
#endif // CP_ALGO_STRUCTURES_CSR_HPP

verify/graph/scc.test.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// @brief Strongly Connected Components
2+
#define PROBLEM "https://judge.yosupo.jp/problem/scc"
3+
#pragma GCC optimize("Ofast,unroll-loops")
4+
#include <iostream>
5+
#include "blazingio/blazingio.min.hpp"
6+
#include "cp-algo/graph/scc.hpp"
7+
#include <bits/stdc++.h>
8+
9+
using namespace std;
10+
using namespace cp_algo::graph;
11+
12+
void solve() {
13+
int n, m;
14+
cin >> n >> m;
15+
digraph g(n);
16+
g.read_edges(m);
17+
auto comps = scc(g);
18+
cout << size(comps) << '\n';
19+
for(auto const& comp: comps.rows() | views::reverse) {
20+
cout << size(comp) << ' ';
21+
for(auto v: comp) {
22+
cout << v << ' ';
23+
}
24+
cout << '\n';
25+
}
26+
}
27+
28+
signed main() {
29+
//freopen("input.txt", "r", stdin);
30+
ios::sync_with_stdio(0);
31+
cin.tie(0);
32+
int t = 1;
33+
//cin >> t;
34+
while(t--) {
35+
solve();
36+
}
37+
}

0 commit comments

Comments
 (0)