//#pragma warning(disable:4996)
//#include <Windows.h>
#include <iostream>
#include <vector>
#include <limits.h>
#include <algorithm>
#include <string>
#include <math.h>
#include <limits.h>
#include <queue>
#include <map>
#include <set>
#include <iomanip>
#include <bitset>
#include <cassert>
#include <random>
#include <functional>
#include <stack>
#include <iomanip>
#include <cassert>
//#include <boost/multiprecision/cpp_int.hpp>
#include <complex>
#include <cstdio>
#include <list>
#include <bitset>
//#include <stdio.h>

//< in.txt > out.txt
using namespace std;
//std::ios::sync_with_stdio(false);
//std::cin.tie(0);
const long long MOD = 1e9 + 7;
const long long INF = 1e18;
typedef long long LL;
typedef long double LD;
//typedef boost::multiprecision::cpp_int bigint;
typedef pair<LL, LL> PLL;
typedef pair<int, int> PI;
typedef pair<LD, LL> pdl;
typedef pair<LD, LD> pdd;
typedef vector<LL> VLL;
typedef vector<VLL> VVLL;
typedef vector<int> VI;
typedef vector<vector<int>> VVI;
typedef unsigned long long ULL;
template<class T>
using pqueue = priority_queue<T, vector<T>, function<bool(T, T)>>;

template<class T>
inline void chmin(T& a, T b) {
	a = min(a, b);
}

template<class T>
inline void chmax(T& a, T b) {
	a = max(a, b);
}

void input();
void solve();

void daminput();
void naive();

void outputinput();

int main() {
	std::cin.tie(0);
	std::ios::sync_with_stdio(false);
	input();
	//daminput();
	solve();
	//naive();
	//outputinput();
	return 0;
}

//////////////////////////////////////////////////
//////////////////////////////////////////////////

int N, M;
VI A, B;

void input() {
	cin >> N >> M;
	A.resize(M);
	B.resize(M);
	for (int m = 0; m < M; m++)cin >> A[m] >> B[m];
}

void daminput() {
}

template<class T>
struct Gedge {
	int src, to;
	T cost;
	int id;
	Gedge(int s, int t, T c, int i = -1) :src(s), to(t), cost(c), id(i) {}
	Gedge(int t, T c) :src(-1), to(t), cost(c), id(-1) {}
};

template<class T>
using WeightedGraph = vector<vector<Gedge<T>>>;
using UnweightedGraph = vector<vector<LL>>;
template<class T>
using Gedges = vector<Gedge<T>>;

template<class T>
struct TNode {
	int parent;
	VI childs;
	T cost;
	int id;
	TNode() :parent(-1), cost(0), id(-1) {};
};
template<class T>
using Tree = vector<TNode<T>>;

template<class T>
void sccbfs(WeightedGraph<T>& g, VI& res, int n, vector<bool>& check) {
	check[n] = true;
	for (auto& e : g[n]) {
		if (!check[e.to]) {
			sccbfs(g, res, e.to, check);
		}
	}
	res.push_back(n);
}

//強連結成分分解
//上流側の強連結成分の番号が小さくなるように番号付け
//分解後のDAGの頂点数を返す
template<class T>
int SCC(WeightedGraph<T>& G, VI& scv)
{
	int N = G.size();
	VI bfs1;
	vector<bool> bfscheck(N, false);
	for (int n = 0; n < N; n++)
	{
		if (!bfscheck[n]) {
			sccbfs(G, bfs1, n, bfscheck);
		}
	}
	WeightedGraph<int> Grev(N);
	for (int n = 0; n < N; n++)
	{
		for (auto& e : G[n]) {
			Grev[e.to].push_back({ n,1 });
		}
	}
	scv.resize(N, -1);
	int next = 0;
	for (int ordi = N - 1; ordi >= 0; ordi--)
	{
		if (scv[bfs1[ordi]] != -1)continue;
		queue<int> q;
		q.push(bfs1[ordi]);
		scv[bfs1[ordi]] = next;
		while (!q.empty()) {
			int cur = q.front();
			q.pop();
			for (auto& e : Grev[cur]) {
				int ch = e.to;
				if (scv[ch] != -1)continue;
				scv[ch] = next;
				q.push(ch);
			}
		}
		next++;
	}
	return next;
}

void solve() {
	WeightedGraph<int> G(N);
	for (int m = 0; m < M; m++) {
		G[A[m]].push_back({ B[m],1 });
	}
	VI scv;
	int K = SCC(G, scv);
	cout << K << "\n";
	vector<set<int>> ans(K);
	for (int n = 0; n < N; n++) {
		ans[scv[n]].insert(n);
	}
	for (int k = 0; k < K; k++) {
		cout << ans[k].size();
		for (int n:ans[k]){
			cout << " " << n;
		}
		cout << "\n";
	}
}

void naive() {
}

void outputinput() {
}

Measured memory_bytes: 71064