#include <bits/stdc++.h>
//#include <chrono>
//#pragma GCC optimize("Ofast")
using namespace std;
#define reps(i,s,n) for(int i = s; i < n; i++)
#define rep(i,n) reps(i,0,n)
#define Rreps(i,n,e) for(int i = n - 1; i >= e; --i)
#define Rrep(i,n) Rreps(i,n,0)
#define ALL(a) a.begin(), a.end()
#define fi first
#define se second
typedef long long ll;
typedef vector<ll> vec;
typedef vector<vec> mat;

ll N,M,H,W,Q,K,A,B;
string S;
typedef pair<ll, ll> P;
const ll INF = (1LL<<60);

void dfs1(int n, mat &G, vec &ord){
    vector<int> iter(n, -1);
    stack<int> sta;
    rep(i, n) {
        if(iter[i] < 0) sta.push(i);
        while (!sta.empty()) {
            int v = sta.top();
            if ((++iter[v]) == G[v].size()) {
                ord.push_back(v);
                sta.pop();
            } else {
                int to = G[v][iter[v]];
                if(iter[to] < 0) sta.push(to);
            }
        }
    }
}
void dfs1_ok(int v, int &k, mat &G, vec &level){
    level[v] = -2;
    for(int to : G[v]){
        if(level[to] == -1) dfs1_ok(to, k, G, level);
    }
    level[v] = k++;
}
void dfs2(int n, mat &G, vec &ord, mat &res){
    vector<bool> used(n, false);
    stack<int> sta;
    Rrep(i, n){
        int s = ord[i];
        if(!used[s]){
            used[s] = true;
            vec temp(0);
            sta.push(s);
            while(!sta.empty()){
                int v = sta.top(); sta.pop();
                temp.push_back(v);
                for(int to : G[v]) {
                    if(!used[to]) {
                        used[to] = true;
                        sta.push(to);
                    }
                }
            }
            res.push_back(temp);
        }
    }
}

void scc(mat &G, mat &res, bool ok){
    int n = G.size();
    mat G_rev(n, vec(0));
    rep(u, n) rep(v, (int)G[u].size()) G_rev[G[u][v]].push_back(u);
    vec ord(0);
    if(!ok){
        dfs1(n, G, ord);
    }else {
        int k(0);
        vec level(n, -1);
        rep(i, n) if (level[i] == -1) dfs1_ok(i, k, G, level);
        rep(i, n) ord[level[i]] = i;
    }
    dfs2(n, G_rev, ord, res);
}

void input(mat &G){
    rep(i,M){
        int a, b;
        scanf("%d%d", &a, &b);
        G[a].push_back(b);
    }
}

void output(mat &scc_list){
    printf("%d\n", (int)scc_list.size());
    for(vec v : scc_list){
        int n = v.size();
        printf("%d ", n);
        rep(i, n) printf("%lld%c", v[i], " \n"[i == n - 1]);
    }
}

void debug(){
    int num(0);
    bool same = true;
    random_device seed;
    mt19937 mt(seed());
    uniform_int_distribution<> rand_v(0, N - 1), rand_M(1, N * 2000);
    while(same && (num++) < (int)1e+5){
        mat G(N, vec(0)), scc_list(0), scc_list_2(0);
        int m = rand_M(mt);
        rep(i, m){
            int a = rand_v(mt), b = rand_v(mt);
            G[a].push_back(b);
        }
        scc(G, scc_list, true);
        scc(G, scc_list_2, false);
        if(scc_list.size() != scc_list_2.size()) same = false;
        if(same){
            rep(i, (int)scc_list.size()){
                if(scc_list[i].size() != scc_list_2[i].size()){
                    same = false;
                    break;
                }
                set<int> s, t;
                for(int a : scc_list[i]) s.insert(a);
                for(int a : scc_list_2[i]) t.insert(a);
                if(s != t) {
                    same = false;
                    break;
                }
            }
        }
        if(num%10000 == 0) cout<<num<<endl;
        if(!same){
            cout<<num<<endl;
            cout<<"5 "<<m<<endl;
            rep(i, 5) {
                for(ll v : G[i]) cout<<i<<' '<<v<<endl;
            }
            output(scc_list);
            output(scc_list_2);
        }
    }
}

int main() {
    cin>>N;
    bool dbg = false;
    if(dbg){
        debug();
    }else {
        cin >> M;
        mat G(N, vec(0)), scc_list(0), scc_list_2(0);
        input(G);
        scc(G, scc_list, false);
        output(scc_list);
    }
}


Measured memory_bytes: 79188