//#pragma GCC optimize("Ofast")
//#pragma GCC optimize("unroll-loops")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using db = double;
using ld = long double;
template<typename T> using V = vector<T>;
template<typename T> using VV = vector<vector<T>>;
#define fs first
#define sc second
#define pb push_back
#define mp make_pair
#define mt make_tuple
#define eb emplace_back
#define lb lower_bound
#define ub upper_bound
#define all(v) (v).begin(),(v).end()
#define siz(v) (ll)(v).size()
#define rep(i,a,n) for(ll i=a;i<(ll)(n);++i)
#define repr(i,a,n) for(ll i=n-1;(ll)a<=i;--i)
#define ENDL '\n'
typedef pair<int,int> Pi;
typedef pair<ll,ll> PL;
constexpr ll mod = 1000000007; // 998244353;
constexpr ll INF = 1000000099;
constexpr ll LINF = (ll)(1e18 +99);
const ld PI = acos((ld)-1);
const vector<ll> dx={-1,1,0,0},dy={0,0,-1,1};
template<typename T,typename U> inline bool chmin(T& t, const U& u){if(t>u){t=u;return 1;}return 0;}
template<typename T,typename U> inline bool chmax(T& t, const U& u){if(t<u){t=u;return 1;}return 0;}
template<typename T> inline T gcd(T a,T b){return b?gcd(b,a%b):a;}

template<typename T,typename Y> inline T mpow(T a, Y n) {
  T res = 1;
  for(;n;n>>=1) {
    if (n & 1) res = res * a;
    a = a * a;
  }
  return res;
}

template <typename T> V<T> prefix_sum(const V<T>& v) {
  int n = v.size();
  V<T> ret(n + 1);
  rep(i, 0, n) ret[i + 1] = ret[i] + v[i];
  return ret;
}

template<typename T>
istream& operator >> (istream& is, vector<T>& vec){
  for(auto&& x: vec) is >> x;
  return is;
}

template<typename T,typename Y>
ostream& operator<<(ostream& os,const pair<T,Y>& p){
  return os<<"{"<<p.fs<<","<<p.sc<<"}";
}

template<typename T> ostream& operator<<(ostream& os,const V<T>& v){
  os<<"{";
  for(auto e:v)os<<e<<",";
  return os<<"}";
}

template<typename ...Args>
void debug(Args&... args){
  for(auto const& x:{args...}){
    cerr<<x<<' ';
  }
  cerr<<ENDL;
}

template<typename T>
struct SCC{
    int n,now=0;
    const vector<vector<T>>& e;
    vector<bool> used;
    vector<int> ind,low,ord,st;//low:圧縮後の頂点番号になる ord:到達順 st:連結成分復元に使うstack
    vector<vector<int>> grps,G;

    void dfs(int v){
        low[v]=now;
        ord[v]=now;
        now++;
        st.push_back(v);
        used[v]=true;
        for(auto i:e[v]){
            if(ord[i]==-1){
                dfs(i);
                low[v]=min(low[v],low[i]);
            }else if(used[i]){//ループなどで到達済みに回ってきた場合
                low[v]=min(low[v],ord[i]);//iが既に到達した場所ならその頂点番号で更新して,戻っていく
            }
        }

        if(low[v]==ord[v]){//戻ってきた==強連結成分発見
            vector<int> gr;
            while(1){
                int u=st.back();st.pop_back();
                gr.push_back(u);
                if(u==v)break;
            }//帰りがけ順に強連結成分をgrに入れる
            for(int i:gr)used[i]=false;//?
            grps.push_back(gr);//強連結成分を入れる
        }
    }

    SCC(const vector<vector<T>>& _e):n(_e.size()),e(_e),used(n,false),low(n),ord(n,-1){
        ind=vector<int>(n,0);//
        for(int i=0;i<n;i++){
            if(ord[i]==-1)dfs(i);//順序未決定==未到達
        }
        reverse(grps.begin(),grps.end());
        for(int i=0;i<(int)grps.size();i++){
            for(auto&& x:grps[i]){
                ind[x]=i;
            }
        }//頂点圧縮
        
        G.resize(grps.size());
        for(int i=0;i<n;i++){
            for(int j=0;j<(int)e[i].size();j++){
                if(ind[i]!=ind[e[i][j]]){
                    G[ind[i]].pb(ind[e[i][j]]);
                }
            }
        }
    }

    vector<vector<int>>& get_scc(){
      return G;
    }
};
//indがSCC後の頂点番号,grpが同成分の頂点をまとめたリストになっている
//grpsに入る閉路内頂点の順番は帰りがけ順

signed main(){
  cin.tie(0);cerr.tie(0);ios::sync_with_stdio(false);
  cout<<fixed<<setprecision(20);
  ll n,m;cin>>n>>m;
  VV<int> e(n,V<int>(0));

  rep(i,0,m){
    int a,b;cin>>a>>b;
    e[a].eb(b);
  }

  SCC<int> s(e);

  cout<<siz(s.grps)<<ENDL;
  for(auto&& v:s.grps){
    cout<<siz(v)<<" ";
    for(auto&& i:v)cout<<i<<" ";
    cout<<ENDL;
  }
}
//! ( . _ . ) ! 
//CHECK overflow,vector_size,what to output?

Measured memory_bytes: 82068