Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ ac-library-python is a Python port of [AtCoder Library (ACL)](https://atcoder.jp
#### Graph

+ [Disjoint Set Union (DSU)](https://github.com/atcoder/ac-library/blob/master/document_en/dsu.md)
+ scc

### Work in progress

Expand All @@ -41,7 +42,6 @@ ac-library-python is a Python port of [AtCoder Library (ACL)](https://atcoder.jp

+ maxflow
+ mincostflow
+ scc
+ twosat

## Install
Expand Down
2 changes: 1 addition & 1 deletion README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ ac-library-pythonは、[AtCoder Library (ACL)](https://atcoder.jp/posts/517)のP
#### グラフ

+ [Disjoint Set Union (DSU)](https://github.com/atcoder/ac-library/blob/master/document_ja/dsu.md)
+ scc

### 準備中

Expand All @@ -39,7 +40,6 @@ ac-library-pythonは、[AtCoder Library (ACL)](https://atcoder.jp/posts/517)のP

+ maxflow
+ mincostflow
+ scc
+ twosat

## インストール
Expand Down
101 changes: 101 additions & 0 deletions atcoder/_scc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import copy
import sys
import typing


class CSR:
def __init__(
self, n: int, edges: typing.List[typing.Tuple[int, int]]) -> None:
self.start = [0] * (n + 1)
self.elist = [0] * len(edges)

for e in edges:
self.start[e[0] + 1] += 1

for i in range(1, n + 1):
self.start[i] += self.start[i - 1]

counter = copy.deepcopy(self.start)
for e in edges:
self.elist[counter[e[0]]] = e[1]
counter[e[0]] += 1


class SCCGraph:
'''
Reference:
R. Tarjan,
Depth-First Search and Linear Graph Algorithms
'''

def __init__(self, n: int) -> None:
self._n = n
self._edges = []

def num_vertices(self) -> int:
return self._n

def add_edge(self, from_vertex: int, to_vertex: int) -> None:
self._edges.append((from_vertex, to_vertex))

def scc_ids(self) -> typing.Tuple[int, typing.List[int]]:
g = CSR(self._n, self._edges)
now_ord = 0
group_num = 0
visited = []
low = [0] * self._n
order = [-1] * self._n
ids = [0] * self._n

sys.setrecursionlimit(max(self._n + 1000, sys.getrecursionlimit()))

def dfs(v: int) -> None:
nonlocal now_ord
nonlocal group_num
nonlocal visited
nonlocal low
nonlocal order
nonlocal ids

low[v] = now_ord
order[v] = now_ord
now_ord += 1
visited.append(v)
for i in range(g.start[v], g.start[v + 1]):
to = g.elist[i]
if order[to] == -1:
dfs(to)
low[v] = min(low[v], low[to])
else:
low[v] = min(low[v], order[to])

if low[v] == order[v]:
while True:
u = visited[-1]
visited.pop()
order[u] = self._n
ids[u] = group_num
if u == v:
break
group_num += 1

for i in range(self._n):
if order[i] == -1:
dfs(i)

for i in range(self._n):
ids[i] = group_num - 1 - ids[i]

return group_num, ids

def scc(self) -> typing.List[typing.List[int]]:
ids = self.scc_ids()
group_num = ids[0]
counts = [0] * group_num
for x in ids[1]:
counts[x] += 1
groups = [[] for _ in range(group_num)]
for i in range(self._n):
groups[ids[1][i]].append(i)

return groups
17 changes: 17 additions & 0 deletions atcoder/scc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import typing

import atcoder._scc


class SCCGraph:
def __init__(self, n: int = 0) -> None:
self._internal = atcoder._scc.SCCGraph(n)

def add_edge(self, from_vertex: int, to_vertex: int) -> None:
n = self._internal.num_vertices()
assert 0 <= from_vertex < n
assert 0 <= to_vertex < n
self._internal.add_edge(from_vertex, to_vertex)

def scc(self) -> typing.List[typing.List[int]]:
return self._internal.scc()
27 changes: 27 additions & 0 deletions example/scc_practice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# https://atcoder.jp/contests/practice2/tasks/practice2_b

import sys

from atcoder.scc import SCCGraph


def main() -> None:
n, m = map(int, sys.stdin.readline().split())
g = SCCGraph(n)

for i in range(m):
u, v = map(int, sys.stdin.readline().split())
g.add_edge(u, v)

scc = g.scc()

print(len(scc))
for v in scc:
print(len(v), end='')
for x in v:
print(f' {x}', end='')
print('')


if __name__ == '__main__':
main()