Pagini recente » Cod sursa (job #1916570) | Cod sursa (job #1780830) | Cod sursa (job #3177574) | Cod sursa (job #2593312) | Cod sursa (job #1999324)
#include <iostream>
#include <fstream>
#include <stack>
#include <vector>
using namespace std;
ifstream in("ctc.in");
ofstream out("ctc.out");
// algoritmul Kosaraju
// O(N+M)
#define ll long long
#define pb push_back
const int inf = 1e9 + 5;
const int NMax = 1e5 + 5;
int N,M,nrComp;
bool inStack[NMax],vis[NMax];
stack<int> st;
vector<int> v[NMax],rev[NMax],comp[NMax];
// nrComp - numarul de componente care s-au gasit pana la un moment dat;
// inStack[i] = true daca nodul i a fost bagat in stiva
// ca rezultat al primei parcurgeri dfs (build);
// vis[i] = true daca nodul i a fost parcurs in cea de-a doua
// parcurgere dfs si deci a fost gasita componenta conexa din care el face parte;
// st - stiva generata prin functia build();
// aceasta are proprietatea ca, fiind date doua noduri u si v,
// daca exista drum de la u la v, dar nu exista drum de la v la u,
// atunci nodul v se afla obligatoriu mai jos in stiva decat nodul u;
// daca exista ambele drumuri, cele doua au o ordine aleatorie;
// v - liste de adiacenta pentru graful dat;
// rev - liste de adiacenta pentru graful transpus;
// comp[i] - contine nodurile din a i-a componenta conexa;
void build(int);
void dfs(int);
int main() {
in>>N>>M;
while (M--) {
int x,y;
in>>x>>y;
v[x].pb(y);
rev[y].pb(x);
}
// se construieste stiva st care va contine toate nodurile exact o data
// si va avea proprietatea enuntata mai sus
for (int i=1;i <= N;++i) {
if (inStack[i]) {
continue;
}
build(i);
}
// se realizeaza dfs-uri pe arcele din graful transpuns in ordine inversa
// a nodurilor din stiva si se formeaza componentele tare conexe;
// faptul ca algoritmul este corect se poate arata prin:
// - urmatorul loop-invariant:
// s-a gasit componenta conexa pentru toate nodurile vizitate anterior
// iar toate nodurile scoase deja din stiva au fost vizitate;
// - urmatoarele observatii:
// * daca exista back arch de la nodul i din dfs la nodul j,
// atunci exista obligatoriu drum de la i la j;
// * daca se merge prin back-arches de la nodul i la alte noduri,
// va exista obligatoriu drum de la i la aceste noduri
// (caci altfel i n-ar fi deasupra lor in stiva)
// * daca un nod k si nodul i se afla in aceeasi componenta,
// dar nu exista back-arch de la i la k, atunci obligatoriu
// va exista un drum de back-arches de la i la k, caz in care
// se va ajnge la k prin dfs si exista drum in graful dat de la i la k
// (conform observatiei anterioare)
while (st.size()) {
int node = st.top();
st.pop();
if (vis[node]) {
continue;
}
++nrComp;
dfs(node);
}
out<<nrComp<<'\n';
for (int i=1;i <= nrComp;++i) {
for (int node : comp[i]) {
out<<node<<' ';
}
out<<'\n';
}
in.close();
out.close();
return 0;
}
void build(int node) {
inStack[node] = true;
for (int nxt : v[node]) {
if (inStack[nxt]) {
continue;
}
build(nxt);
}
st.push(node);
}
void dfs(int node) {
vis[node] = true;
comp[nrComp].pb(node);
for (int nxt : rev[node]) {
if (vis[nxt]) {
continue;
}
dfs(nxt);
}
}