#include <cstdio>
#define NMAX 100100
using namespace std;
FILE*fout=fopen("ctc.out", "w");
char uz[NMAX];
char v1[NMAX], v2[NMAX];
int nr[NMAX];
int n, m, nr_ctc;
struct nod
{int inf;
struct nod *urm;
};
typedef struct nod *Lista;
Lista A[NMAX], AT[NMAX], CTC[NMAX];
void inserare(Lista&, Lista, int);
void citire(); void DFS(int, char[], int); void rez();
int main()
{
citire();
rez();
return 0;
}
void citire()
{
FILE*fin=fopen("ctc.in", "r");
fscanf(fin, "%d %d", &n, &m);
int i, x, y;
for (i=1; i<=m; ++i)
{
fscanf(fin, "%d %d", &x, &y);
//am arc de la x la y
inserare (A[x], NULL, y);
//in graful transpus, am arc de la y la x
inserare (AT[y], NULL, x);
}
fclose(fin);
return;
}
void rez()
{
//DFS pentru graful initial
//DFS pentru graful transpus
int i, j;
Lista q;
for (i=1; i<=n; ++i)
if (!uz[i])
{
++nr_ctc;
DFS (i, v1, 0);
DFS (i, v2, 1);
for (j=1; j<=nr[0]; ++j)
{
if (v1[nr[j]] && v2[nr[j]] && !uz[nr[j]])
{
inserare(CTC[nr_ctc], NULL, nr[j]);
uz[nr[j]]=nr_ctc;//se afla intr-o componenta conexa
}
v1[nr[j]]=v2[nr[j]]=0;
}
nr[0]=0;
}
//afisez componentele tare conexe
fprintf(fout, "%d\n", nr_ctc);
for (i=1; i<=nr_ctc; ++i)
{
for (q=CTC[i]; q; q=q->urm)
fprintf(fout, "%d ", q->inf);
fprintf(fout, "\n");
}
fclose(fout);
return;
}
void DFS (int vf, char v[], int care)
{
Lista q;
if (!care) nr[++nr[0]]=vf;
v[vf]=1;//acest varf este accesibil
if (!care) q=A[vf];
else q=AT[vf];
for ( ; q; q=q->urm)
if (!v[q->inf])
DFS (q->inf, v, care);
return;
}
void inserare (Lista &prim, Lista p, int x)
{
Lista q;
q=new nod;
q->inf=x;
if (p)
{
q->urm=p->urm;
p->urm=q;
}
else
{
q->urm=prim;
prim=q;
}
return;
}