Problem:
Score : 100 points

Problem Statement

You are given a directed graph with N vertices and M edges, not necessarily simple. The i-th edge is oriented from the vertex a_i to the vertex b_i.
Divide this graph into strongly connected components and print them in their topological order.

Constraints

1 \leq N \leq 500,000

1 \leq M \leq 500,000

0 \leq a_i, b_i < N

Input

Input is given from Standard Input in the following format:

N M
a_0 b_0
a_1 b_1
:
a_{M - 1} b_{M - 1}

Output

Print 1+K lines, where K is the number of strongly connected components.
Print K on the first line.
Print the information of each strongly connected component in next K lines in the following format, where l is the number of vertices in the strongly connected component and v_i is the index of the vertex in it.

l v_0 v_1 ... v_{l-1}

Here, for each edge (a_i, b_i), b_i should not appear in earlier line than a_i.

If there are multiple correct output, print any of them.

Sample Input 1

6 7
1 4
5 2
3 0
5 5
4 1
0 3
4 2

Sample Output 1

4
1 5
2 4 1
1 2
2 3 0

Solution in PyPy3 (7.3.0) (judge verdict: Accepted)

class SCC:
  '''
  SCC class with non-recursive DFS.
  '''
  def __init__(self,N):
    self.N = N
    self.G1 = [[] for _ in range(N)]
    self.G2 = [[] for _ in range(N)]
  
  def add_edge(self,a,b):
    self.G1[a].append(b)
    self.G2[b].append(a)
    
  def scc(self):
    self.seen = [0]*self.N
    self.postorder=[-1]*self.N
    self.order = 0
    for i in range(self.N):
      if self.seen[i]:continue
      self._dfs(i)

    self.seen = [0]*self.N
    scclist = []
    for i in self._argsort(self.postorder,reverse=True):
      if self.seen[i]:continue
      cc = self._dfs2(i)
      scclist.append(cc)
    
    return scclist
    
  def _argsort(self,arr,reverse=False):
    shift = self.N.bit_length()+2
    tmp = sorted([arr[i]<<shift | i for i in range(len(arr))],reverse=reverse)
    mask = (1<<shift) - 1
    return [tmp[i] & mask for i in range(len(arr))]
  
  def _dfs(self,v0):
    todo = [~v0, v0]
    while todo:
      v = todo.pop()
      if v >= 0:
        self.seen[v] = 1
        for next_v in self.G1[v]:
          if self.seen[next_v]: continue
          todo.append(~next_v)
          todo.append(next_v)
      else:
        if self.postorder[~v] == -1:
          self.postorder[~v] = self.order
          self.order += 1
    return
  
  def _dfs2(self,v):
    todo = [v]
    self.seen[v] = 1
    cc = [v]
    while todo:
      v = todo.pop()
      for next_v in self.G2[v]:
        if self.seen[next_v]: continue
        self.seen[next_v] = 1
        todo.append(next_v)
        cc.append(next_v)
    return cc

def main():
  N,M = map(int,input().split())
  scc = SCC(N)
  for i in range(M):
    a,b=map(int,input().split())
    scc.add_edge(a,b)
  scclist = scc.scc()
  print(len(scclist))
  for cc in scclist:
    print(len(cc),*cc)
    
if __name__=="__main__":
  main()