#include <bits/stdc++.h>

using namespace std;
using ll = long long;
using Graph = vector<vector<int>>;
using WGraph = vector<vector<pair<int, ll>>>;

template<class T>inline bool chmax(T &a, const T &b) { if (b > a) { a = b; return true; } return false; }
template<class T>inline bool chmin(T &a, const T &b) { if (b < a) { a = b; return true; } return false; }

constexpr int INF = 1e9;
constexpr int MOD = 1e9 + 7;
constexpr long long LINF = 1e18;
constexpr double EPS = 1e-10;
constexpr double PI = M_PI;
class SCC {
    private:
        int N;
        Graph G, revG;
        std::vector<int> comp, ord;
        void dfs(int cur, std::vector<bool> &visited) {
            visited[cur] = true;
            for (int nx : G[cur]) {
                if (!visited[nx]) dfs(nx, visited);
            }
            ord.push_back(cur);
        }
        void rdfs(int cur, int group) {
            comp[cur] = group;
            for (int nx : revG[cur]) {
                if (comp[nx] == -1) rdfs(nx, group);
            }
        }
    public:
        SCC(const Graph &g) : N((int)g.size()), G(g), revG(N), comp(N, -1) {
            for (int i=0; i<N; ++i) {
                for (int j : G[i]) {
                    revG[j].push_back(i);
                }
            }
        }
        // build and retun number of group
        int build() {
            std::vector<bool> visited(N, false);
            for (int i=0; i<N; ++i) {
                if (!visited[i]) dfs(i, visited);
            }
            std::reverse(ord.begin(), ord.end());
            int group = 0;
            for (int idx : ord) {
                if (comp[idx] == -1) rdfs(idx, group++);
            }
            return group;
        }
        int operator[](int idx) { return comp[idx]; }
};

std::vector<int> topologicalSort(const Graph &G) {
    int n = (int)G.size();
    std::vector<int> cntIn(n);
    for (auto vec : G) {
        for (int x : vec) {
            ++cntIn[x];
        }
    }

    std::vector<int> res;
    std::stack<int> st;
    for (int i=0; i<n; i++) {
        if (cntIn[i] == 0) st.push(i);
    }
    while (!st.empty()) {
        int cur = st.top(); st.pop();
        res.push_back(cur);
        for (int x : G[cur]) {
            --cntIn[x];
            if (cntIn[x] == 0) st.push(x);
        }
    }

    return res;
}

void solve() {
    int N, M;
    cin >> N >> M;
    Graph G(N);
    for (int i=0; i<M; ++i) {
        int a, b;
        cin >> a >> b;
        G[a].push_back(b);
    }

    SCC scc(G);
    int n = scc.build();
    vector<vector<int>> group(n);
    for (int i=0; i<N; ++i) group[scc[i]].push_back(i);
    Graph GG(n);
    for (int i=0; i<N; ++i) {
        for (int x : G[i]) {
            if (scc[i] != scc[x]) GG[scc[i]].push_back(scc[x]);
        }
    }
    auto ts = topologicalSort(GG);
    cout << n << '\n';
    for (int x : ts) {
        cout << group[x].size() << ' ';
        for (int i=0; i<(int)group[x].size(); ++i) {
            cout << group[x][i];
            if (i == (int)group[x].size()-1) cout << '\n';
            else cout << ' ';
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout << fixed << setprecision(20);

    solve();

    return 0;
}


Measured memory_bytes: 126772