#include <fstream>
#include <vector>
#include <algorithm>
#include <cstring>
class Graf
{
private:
static constexpr int NMAX = 100009;
const int N, M;
std::vector<int> Adiacenta[NMAX]; // liste de adiacenta
public:
Graf(int, int);
void AdMuchie(int, int);
void Bfs(int, int*);
int Dfs();
void Biconex(std::vector<std::vector<int>>&);
void CompTareConexe(std::vector<int>*, std::vector<std::vector<int>>&, int&);
static constexpr int getNMAX() { return Graf::NMAX; }
private:
void Dfs_fHelper(int, bool*);
void Biconex_fHelper(int, int, std::pair<int, int>*, int&, int*, int*, std::vector<std::vector<int>>&);
void CompTareConexe_Dfs(int, bool*, int*, int&);
void CompTareConexe_t_Dfs(int, bool*, std::vector<int>*, std::vector<std::vector<int>>&, int);
};
Graf::Graf(int n, int m)
: N(n), M(m)
{
}
void Graf::AdMuchie(int ini, int fin)
{
Adiacenta[ini].push_back(fin);
}
void Graf::Bfs(int S, int* nrArce)
{
int coada[NMAX], st = 0, dr = 0;
for (int i = 1; i <= N; ++i)
nrArce[i] = -1;
nrArce[S] = 0;
coada[st] = S;
while (st <= dr)
{
int curent = coada[st++];
for (auto it = Adiacenta[curent].begin(); it != Adiacenta[curent].end(); ++it)
if(nrArce[*it] == -1)
{
coada[++dr] = *it;
nrArce[*it] = nrArce[curent] + 1;
}
}
}
int Graf::Dfs()
{
bool vizitat[NMAX];
int nrComp = 0;
memset(vizitat, 0, NMAX * sizeof(bool));
for (int i = 1; i <= N; ++i)
if (!vizitat[i])
{
++nrComp;
Dfs_fHelper(i, vizitat);
}
return nrComp;
}
void Graf::Dfs_fHelper(int curent, bool* vizitat)
{
vizitat[curent] = true;
for (auto it = Adiacenta[curent].begin(); it != Adiacenta[curent].end(); ++it)
if (!vizitat[*it])
Dfs_fHelper(*it, vizitat);
}
void Graf::Biconex(std::vector<std::vector<int>>& compBic)
{
std::pair<int, int> Stiva[NMAX * 2];
int stVf = 0;
int nivelInArbDFS[NMAX]; // folosit si ca vizitat[]
int nivelMinVecini[NMAX];
nivelInArbDFS[0] = 0;
Biconex_fHelper(1, 0, Stiva, stVf, nivelInArbDFS, nivelMinVecini, compBic);
}
void Graf::Biconex_fHelper(int nod, int parinte, std::pair<int, int>* Stiva, int& stVf, int* nivelInArbDFS, int* nivelMinVecini, std::vector<std::vector<int>>& compBic)
{
nivelInArbDFS[nod] = nivelInArbDFS[parinte] + 1;
nivelMinVecini[nod] = NMAX;
for (int& vecin : Adiacenta[nod])
{
if (nivelInArbDFS[vecin]) // daca e vizitat
{
if (nivelMinVecini[nod] > nivelInArbDFS[vecin] && vecin != parinte)
nivelMinVecini[nod] = nivelInArbDFS[vecin];
}
else
{
Stiva[++stVf] = {nod, vecin};
Biconex_fHelper(vecin, nod, Stiva, stVf, nivelInArbDFS, nivelMinVecini, compBic);
if (nivelMinVecini[nod] > nivelMinVecini[vecin])
nivelMinVecini[nod] = nivelMinVecini[vecin];
if (nivelMinVecini[vecin] >= nivelInArbDFS[nod])
{
compBic.push_back({});
while (Stiva[stVf].first != nod)
compBic.back().push_back(Stiva[stVf--].second);
compBic.back().push_back(Stiva[stVf].second); // muchia din 'nod', intreaga
compBic.back().push_back(Stiva[stVf].first); //
--stVf;
}
}
}
}
void Graf::CompTareConexe(std::vector<int>* t_Adiacenta, std::vector<std::vector<int>>& compTC, int& nrComp)
{
int stivaTopo[NMAX], stVf = 0; // stivaTopo - stiva de noduri in care nodurile sunt inserate in ordinea topologica inversata
bool vizitat[NMAX];
memset(vizitat, 0, (N + 1) * sizeof(bool));
for (int nod = 1; nod <= N; ++nod)
if (!vizitat[nod])
CompTareConexe_Dfs(nod, vizitat, stivaTopo, stVf);
compTC.resize(N);
nrComp = 0;
memset(vizitat, 0, (N + 1) * sizeof(bool));
while (stVf)
{
if (!vizitat[stivaTopo[stVf]])
CompTareConexe_t_Dfs(stivaTopo[stVf], vizitat, t_Adiacenta, compTC, nrComp++);
--stVf;
}
}
void Graf::CompTareConexe_Dfs(int nod, bool* vizitat, int* stivaTopo, int& stVf)
{
vizitat[nod] = true;
for (auto& vecin : Adiacenta[nod])
if (!vizitat[vecin])
CompTareConexe_Dfs(vecin, vizitat, stivaTopo, stVf);
stivaTopo[++stVf] = nod;
}
void Graf::CompTareConexe_t_Dfs(int nod, bool* vizitat, std::vector<int>* t_Adiacenta, std::vector<std::vector<int>>& compTC, int nrComp)
{
vizitat[nod] = true;
for (auto& vecin : t_Adiacenta[nod])
if (!vizitat[vecin])
CompTareConexe_t_Dfs(vecin, vizitat, t_Adiacenta, compTC, nrComp);
compTC[nrComp].push_back(nod);
}
int main()
{
std::ifstream f("ctc.in");
std::ofstream g("ctc.out");
int N, M;
std::vector<int> t_Adiacenta[Graf::getNMAX()]; // Transpus
f >> N >> M;
Graf graf(N, M);
for (int x, y, i = 0; i < M; ++i)
{
f >> x >> y;
graf.AdMuchie(x, y);
t_Adiacenta[y].push_back(x);
}
std::vector<std::vector<int>> compTC;
int nrComp;
graf.CompTareConexe(t_Adiacenta, compTC, nrComp);
g << nrComp << '\n';
for (auto& comp : compTC)
{
for (int& nod : comp)
g << nod << ' ';
g << '\n';
}
return 0;
}