#include <iostream>
#include <string>
#include <vector>
#include <array>
#include <queue>
#include <deque>
#include <algorithm>
#include <set>
#include <map>
#include <bitset>
#include <cmath>
#include <functional>
#include <cassert>
#include <iomanip>
#define vll vector<ll>
#define vvvl vector<vvl>
#define vvl vector<vector<ll>>
#define VV(a, b, c, d) vector<vector<d>>(a, vector<d>(b, c))
#define VVV(a, b, c, d) vector<vvl>(a, vvl(b, vll (c, d)));
#define re(c, b) for(ll c=0;c<b;c++)
#define all(obj) (obj).begin(), (obj).end()
typedef long long int ll;
typedef long double ld;
using namespace std;

struct edge{
  ll from, to, weight;
  edge(ll f, ll t, ll w=1):from(f), to(t), weight(w){}
};
struct node{
  vector<ll> to, node_list;
  node(){}
};
//有向グラフ->有向の木状グラフへ
//変換後の頂点は元の頂点のリストを持つ
struct SCC{
  ll N=0;
  vector<vector<edge>> G, rG;
  vector<bool> used;
  vector<ll> cmp, vs;
  vector<node> sG;
  SCC(ll V){
    N = V;
    G.resize(N);
    rG.resize(N);
    init();
  }
  void init(){
    used.resize(N, false);
    cmp.resize(N, 0);
  }
  void add_edge(ll from, ll to, ll w = 1){
    G[from].push_back(edge{from, to, w});
    rG[to].push_back(edge{to, from, w});
  }
  void dfs(ll now){
    used[now] = true;
    for(auto e:G[now]) if(!used[e.to]) dfs(e.to);
    vs.push_back(now);
  }
  void rdfs(ll now, ll k){
    used[now] = true;
    cmp[now] = k;
    for(auto e:rG[now]) if(!used[e.to]) rdfs(e.to, k);
  }
  vector<node> scc(){
    init();
    vs.clear();
    for(int i=0;i<N;i++) if(!used[i]) dfs(i);
    for(int i=0;i<N;i++) used[i] = false;
    ll k = 0;
    for(int i=vs.size()-1;i>=0;i--) if(!used[vs[i]]) rdfs(vs[i], k++);
    sG.resize(k);
    for(int i=0;i<N;i++) {
      used[i] = false;
      sG[cmp[i]].node_list.push_back(i);
    }
    for(int i=0;i<k;i++){
      for(auto v:sG[i].node_list) for(auto to:G[v]) used[cmp[to.to]] = 1;
      for(auto v:sG[i].node_list){
        for(auto to:G[v]){
          if(cmp[to.to]==i) continue;
          if(!used[cmp[to.to]]) continue;
          sG[i].to.push_back(cmp[to.to]);
          used[cmp[to.to]] = 0;
        }
      }
    }
    return sG;
  }
};


vll tps(ll n, vvl G){
  deque<ll> candi;
  vll use(n+1, 0);
  for(int i=0;i<n;i++){
    for(auto to:G[i]){
      use[to]++;
    }
  }
  for(int i=0;i<n;i++) if(!use[i]) candi.push_back(i);
  vll cnt(n+1, 0);//使われた回数
  vll ret;
  while(!candi.empty()){
    ll num = candi.front();candi.pop_front();
    ret.push_back(num);
    for(auto to:G[num]){
      cnt[to]++;
      if(cnt[to]==use[to]){
        candi.push_back(to);
      }
    }
  }
  return ret;
}

int main(int argc, char const *argv[]) {
  ll n, m;scanf("%lld %lld", &n, &m);
  SCC scc(n);
  for(int i=0;i<m;i++){
    ll a, b;scanf("%lld %lld", &a, &b);
    scc.add_edge(a, b);
  }
  vector<node> G = scc.scc();
  ll N = G.size();
  vvl g(N);

  for(int i=0;i<N;i++){
    for(auto to:G[i].to){
      g[i].push_back(to);
    }
  }
  vll x = tps(N, g);
  printf("%lld\n", N);
  for(auto v:x){
    ll M = G[v].node_list.size();
    if(M==0) continue;
    printf("%lld ", M);
    for(int j=0;j<M-1;j++) printf("%lld ", G[v].node_list[j]);
    printf("%lld\n", G[v].node_list[M-1]);
  }
  return 0;
}


Measured memory_bytes: 224712