#include <iostream>
#include <bits/stdc++.h>
using namespace std;
ifstream in("sortaret.in.txt");
ofstream out("sortaret.out.txt");
class Graph
{
private:
int nodes, edges;
vector<vector<int>> AdjList;
vector<vector<int>> AdjListTranspose; ///muchiile in ordine inversa
public:
Graph(int nodes = 0, int edges = 0)
{
this->nodes = nodes;
this->edges = edges;
}
void setNoNodes(int &n){this->edges = n;}
void setNoEdges(int &n){this->nodes = n;}
void addEdge(int first, int second){AdjList[first].push_back(second); AdjListTranspose[second].push_back(first);}
int getNoNodes(){return this->nodes;}
int getNoEdges(){return this->edges;}
void readEdges(istream &in);
void DFS(int, vector<bool> &, vector<int> &, int &);
void DFStranspose(int, vector<int> &, int &);
void KOSARAJU();
void Sortaret();
void Sortaret_Visit(int, vector<bool>&, stack<int>&);
~Graph()
{
this->nodes = 0;
this->edges = 0;
}
};
void Graph :: readEdges(istream &in)
{
int first, second;
AdjList.resize(nodes + 1);
AdjListTranspose.resize(nodes + 1);
for (int i = 0; i < this->edges; i++)
{
in >> first >> second;
//out<<first<<" "<<second<<" "<<"\n";
addEdge(first, second);
}
}
void Graph::DFS(int src, vector<bool> &MarcareInOrdine, vector<int>& Stack, int& indx)
{
MarcareInOrdine[src] = true; //Il marchez (nodul sursa)
for(int i = 0; i < AdjList[src].size(); ++i)
{ //trec prin toti vecinii, iar daca nodul imediat urmator
//din lista nu a fost vizitat, continui parcurgerea.
//1-2-6-!7!-5-!4!-!8!- (!5,6!) - !3! - (!2,1!)
if(!MarcareInOrdine[ AdjList[src][i] ])
DFS(AdjList[src][i], MarcareInOrdine, Stack, indx);
}
//introduc in stiva nodurile la care ajung si nu mai au vecini nevizitati.
//out<<src<<' ';
Stack[++indx] = src;
} //DE CE STACK.push_back() nu salveaza?
void Graph::DFStranspose(int src, vector<int>& MarcareOrdineInversa, int& NoCTC)
{
MarcareOrdineInversa[src] = NoCTC; //marchez nodul && retin din care componenta conexa face parte
for(int i = 0; i < AdjListTranspose[src].size(); ++i)
{
if( !MarcareOrdineInversa[AdjListTranspose[src][i]] )
DFStranspose(AdjListTranspose[src][i], MarcareOrdineInversa, NoCTC);
}
}
void Graph::KOSARAJU()
{
int ContorCTC = 1, stackIndex = 0;
vector<bool> MarcareInOrdine(nodes+1, false);
vector<int> MarcareInOrdineInversa(nodes+1, 0);
vector<int> Stack(nodes+2);
Stack.push_back(0);
for(int i = 1; i<=nodes; ++i) //pentru fiecare nod
{
if( !MarcareInOrdine[i] ) //Daca nu a fost vizitat inca
DFS(i, MarcareInOrdine, Stack, stackIndex); //DFS marcand toti succesorii sai
}
for(int i = nodes; i >= 1; --i)
{
if( !MarcareInOrdineInversa[Stack[i]] ) //daca vecinul din lista inversa (transpose) nu a fost vizitat
{
DFStranspose(Stack[i], MarcareInOrdineInversa, ContorCTC);
ContorCTC++;
}
}
out<<ContorCTC-1<<'\n';
/*
for(int i = 1; i<= ContorCTC; ++i)
{
for(int j = 1; j<=MarcareInOrdineInversa.size(); ++i)
{
if(MarcareInOrdineInversa[j] == i)
out<<j<<' ';
}
out<<'\n';
}
*/
int node_curr, i,j;
for(i = 1; i <= ContorCTC; ++i)
node_curr =0;
for(j = 1; j <= MarcareInOrdineInversa.size(); ++j)
{
if(i == MarcareInOrdineInversa[j])
out<<MarcareInOrdineInversa[j]<<' ';
}
out<<'\n';
}
void Graph::Sortaret_Visit(int node_curr, vector<bool>& visited, stack<int>& Stack)
{
visited[node_curr] = 1;
//recursiv, apelam pentru toti vecinii
vector<int>::iterator itr;
for(itr = AdjList[node_curr].begin(); itr != AdjList[node_curr].end(); ++itr)
if(!visited[*itr])
Sortaret_Visit(*itr, visited, Stack);
Stack.push(node_curr);
}
void Graph::Sortaret()
{
stack<int> Stack;
vector<bool> Visited(nodes+2, 0);
for(int i = 1; i <= nodes; ++i)
if(!Visited[i])
Sortaret_Visit(i, Visited, Stack);
while(!Stack.empty())
{
out<<Stack.top()<<' ';
Stack.pop();
}
}
int main()
{ ///CTC
/* int N, M;
in>>N>>M;
Graph g(N, M);
g.readEdges(in);
g.KOSARAJU();
*/
///sortaret
int N, M;
in>>N, M;
Graph g(N,M);
g.readEdges(in);
g.Sortaret();
return 0;
}