forked from NASU41/AtCoderLibraryForJava
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSCC.java
More file actions
141 lines (133 loc) · 4.33 KB
/
SCC.java
File metadata and controls
141 lines (133 loc) · 4.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/**
* @verified https://atcoder.jp/contests/practice2/tasks/practice2_g
*/
class SCC {
static class Edge {
int from, to;
public Edge(int from, int to) {
this.from = from; this.to = to;
}
}
final int n;
int m;
final java.util.ArrayList<Edge> unorderedEdges;
final int[] start;
final int[] ids;
boolean hasBuilt = false;
public SCC(int n) {
this.n = n;
this.unorderedEdges = new java.util.ArrayList<>();
this.start = new int[n + 1];
this.ids = new int[n];
}
public void addEdge(int from, int to) {
rangeCheck(from);
rangeCheck(to);
unorderedEdges.add(new Edge(from, to));
start[from + 1]++;
this.m++;
}
public int id(int i) {
if (!hasBuilt) {
throw new UnsupportedOperationException(
"Graph hasn't been built."
);
}
rangeCheck(i);
return ids[i];
}
public int[][] build() {
for (int i = 1; i <= n; i++) {
start[i] += start[i - 1];
}
Edge[] orderedEdges = new Edge[m];
int[] count = new int[n + 1];
System.arraycopy(start, 0, count, 0, n + 1);
for (Edge e : unorderedEdges) {
orderedEdges[count[e.from]++] = e;
}
int nowOrd = 0;
int groupNum = 0;
int k = 0;
// parent
int[] par = new int[n];
int[] vis = new int[n];
int[] low = new int[n];
int[] ord = new int[n];
java.util.Arrays.fill(ord, -1);
// u = lower32(stack[i]) : visiting vertex
// j = upper32(stack[i]) : jth child
long[] stack = new long[n];
// size of stack
int ptr = 0;
// non-recursional DFS
for (int i = 0; i < n; i++) {
if (ord[i] >= 0) continue;
par[i] = -1;
// vertex i, 0th child.
stack[ptr++] = 0l << 32 | i;
// stack is not empty
while (ptr > 0) {
// last element
long p = stack[--ptr];
// vertex
int u = (int) (p & 0xffff_ffffl);
// jth child
int j = (int) (p >>> 32);
if (j == 0) { // first visit
low[u] = ord[u] = nowOrd++;
vis[k++] = u;
}
if (start[u] + j < count[u]) { // there are more children
// jth child
int to = orderedEdges[start[u] + j].to;
// incr children counter
stack[ptr++] += 1l << 32;
if (ord[to] == -1) { // new vertex
stack[ptr++] = 0l << 32 | to;
par[to] = u;
} else { // backward edge
low[u] = Math.min(low[u], ord[to]);
}
} else { // no more children (leaving)
while (j --> 0) {
int to = orderedEdges[start[u] + j].to;
// update lowlink
if (par[to] == u) low[u] = Math.min(low[u], low[to]);
}
if (low[u] == ord[u]) { // root of a component
while (true) { // gathering verticies
int v = vis[--k];
ord[v] = n;
ids[v] = groupNum;
if (v == u) break;
}
groupNum++; // incr the number of components
}
}
}
}
for (int i = 0; i < n; i++) {
ids[i] = groupNum - 1 - ids[i];
}
int[] counts = new int[groupNum];
for (int x : ids) counts[x]++;
int[][] groups = new int[groupNum][];
for (int i = 0; i < groupNum; i++) {
groups[i] = new int[counts[i]];
}
for (int i = 0; i < n; i++) {
int cmp = ids[i];
groups[cmp][--counts[cmp]] = i;
}
hasBuilt = true;
return groups;
}
private void rangeCheck(int i) {
if (i < 0 || i >= n) {
throw new IndexOutOfBoundsException(
String.format("Index %d out of bounds for length %d", i, n)
);
}
}
}