class UnionFind():
    def __init__(self, n=0):
        self.d = [-1]*n
    
    def find(self, x):
        if self.d[x] < 0: return x
        self.d[x] = self.find(self.d[x])
        return self.d[x]

    def unite(self, x, y):
        x,y=self.find(x),self.find(y)
        if x==y:return False
        if self.d[x] > self.d[y]: x,y=y,x
        self.d[x] += self.d[y]
        self.d[y] = x
        return True

    def same(self, x,y): return self.find(x)==self.find(y)
    def size(self,x): return -self.d[self.find(x)]
    
def dfs(v):
    global last_ptr
    SEEN[v] = 1
    for next_v in G1[v]:
      if SEEN[next_v]:continue
      dfs(next_v)
    last_order[v] = last_ptr
    last_ptr += 1
    
def dfs2(v,res):
    seen[v] = 1
    res.append(v)
    for next_v in G2[v]:
      if last_order[next_v] > last_order[v] or last_order[next_v] == -1:continue
      if seen[next_v]:continue
      dfs2(next_v,res)
    return res
  
def toposort(inc, out):
    S = {i for i, c in enumerate(inc) if c == 0}
    L = []
    while S:
        n = S.pop()
        L.append(n)
        for m in out[n]:
            inc[m] -= 1
            if inc[m] == 0:
                S.add(m)
    return L

N,M=map(int,input().split())
G1=[[] for _ in range(N)]
G2=[[] for _ in range(N)]
for i in range(M):
  a,b = map(int,input().split())
  G1[a].append(b)
  G2[b].append(a)
  
inorder = list(zip(*sorted(enumerate([len(G2[i]) for i in range(N)]),reverse=True)))[0]

SEEN = [0]*N
scc = {}
uf = UnionFind(N)
for i in inorder:
  if SEEN[i]:continue
  last_order=[-1]*N
  last_ptr = 0
  dfs(i)
  order = list(zip(*sorted(enumerate(last_order),key=lambda x:x[1],reverse=True)))[0]
  seen = [0]*N
  for j in order:
    if last_order[j]==-1:break
    if seen[j]:continue
    res = dfs2(j,[])
    if len(res)>1:
      for i in range(len(res)-1):
        v1,v2 = res[i:i+2]
        uf.unite(v1,v2)
    scc[uf.find(res[0])] = res

G3 = [[] for _ in range(N)]
inc = [0]*N
for v1 in scc.keys():
  for v2 in scc[v1]:
    if v1 != v2: uf.unite(v1,v2)

for c in scc.keys():
  for v1 in scc[c]:
    for v2 in G1[v1]:
      if not uf.same(v1,v2):
        v1,v2 = map(uf.find,(v1,v2))
        G3[v1].append(v2)
        inc[v2] += 1
        
L = toposort(inc, G3)
for c in L:
  if uf.d[c] < 0:
    print(len(scc[c]),*scc[c])

### FIXED CODE ###

N,M = map(int,input().split())
G1 = [[] for _ in range(N)]
G2 = [[] for _ in range(N)]
for i in range(M):
  a,b=map(int,input().split())
  if a == b:continue
  G1[a].append(b)
  G2[b].append(a)

def dfs(a0):
    global order
    todo = [~a0, a0]
    while todo:
        a = todo.pop()
        if a >= 0:
            seen[a] = 1
            for b in G1[a]:
                if seen[b]: continue
                todo.append(~b)
                todo.append(b)
        else:
          if postorder[~a] == -1:
            postorder[~a] = order
            order += 1
    return
  
def dfs2(v):
    todo = [v]
    seen[v] = 1
    cc = [v]
    while todo:
        v = todo.pop()
        for next_v in G2[v]:
            if seen[next_v]: continue
            seen[next_v] = 1
            todo.append(next_v)
            cc.append(next_v)
    return cc
  
seen = [0]*N
postorder=[-1]*N
order = 0
for i in range(N):
  if seen[i]:continue
  dfs(i)

seen = [0]*N
scc = []
for i,_ in sorted(enumerate(postorder),key=lambda x:x[1],reverse=True):
  if seen[i]:continue
  cc = dfs2(i)
  scc.append(cc)
  
print(len(scc))
for cc in scc:
  print(len(cc),*cc)