#include <bits/stdc++.h>
using namespace std;
class Graf
{
int nrNoduri;
int nrMuchii;
int s_bfs; ///nodul s din exercitiul bfs
vector<vector<int>> lsAd; ///lista de adiacenta a grafului
public:
Graf(char* fisierIntrare={"bfs.in"});///constructor pentru problema bfs
Graf(bool orientat,ifstream &in);///constructor pentru exercitiul DFS (si poate altele)
~Graf();
void BFS(char* fisierIesire={"bfs.out"});///primul exercitiu(afiseaza intr-un fisier lungimea drumurilor de la un nod la restul)
void DFS(char* fisierIesire="dfs.out");
void CompBic(ofstream &out);///metoda principala a exercitiului componente biconexe(face dfs din noduri)
void CompBicTimp(int nodCrt, int &nrComp, int* parinte,int* timpIntr, int* timpMin, stack<pair<int,int>>* stiva, set<int>* noduriComp);///metoda care calculeaza timpii fiecarui nod
void CTC(ofstream &out);///metoda principala a exercitiului CTC(Componente tare conexe)
void CTCCalc(int nodCrt, int &nrComp,int* timpIntr, int* timpMin, stack<int>* stiva, set<int>* noduriComp, bool* inStiva);///metoda care calculeaza efectiv timpii pentru CTC
};
Graf::Graf(char* fisierIntrare)
{
ifstream in(fisierIntrare);
int x, y;
in>>nrNoduri>>nrMuchii>>s_bfs;
lsAd.resize(nrNoduri+1); ///un fel de initializare a listei de adiacenta in constructor
for (int i = 0; i < nrMuchii; i++)
{
in>>x>>y;
lsAd[x].push_back(y);
}
in.close();
}
Graf::~Graf()
{
lsAd.clear();
}
Graf::Graf(bool orientat,ifstream &in)
{
int x, y;
in>>nrNoduri>>nrMuchii;
lsAd.resize(nrNoduri+1);
for (int i = 0; i < nrMuchii; i++)
{
in>>x>>y;
lsAd[x].push_back(y);
if (!orientat)
lsAd[y].push_back(x);
}
}
void Graf::DFS(char* fisierIesire)
{
stack<int> stiva;
int nrComponente = 0;
bool* nodMarcat = new bool[nrNoduri+1]();///initializat cu false pentru toate nodurile
for (int i = 1; i <= nrNoduri; i++)
{
if (!nodMarcat[i])
{
stiva.push(i);
nodMarcat[i] = true;
int nodCrt;
while (!stiva.empty())
{
nodCrt = stiva.top();
stiva.pop();
for (auto nodAd : lsAd[nodCrt])
{
if (!nodMarcat[nodAd])
{
nodMarcat[nodAd] = true;
stiva.push(nodAd);
}
}
}
nrComponente++;
}
}
delete[] nodMarcat;
///scrierea in fisier
ofstream out(fisierIesire);
out<<nrComponente;
out.close();
}
void Graf::BFS(char* fisierIesire)
{
queue<int> coada;
bool* nodVizitat = new bool[nrNoduri+1];///array-ul cu care verific daca a fost vizitat un nod
int* distNod = new int[nrNoduri+1];///nr muchii catre fiecare nod
for (int i = 1; i <= nrNoduri; i++)
{
distNod[i] = (i == s_bfs ? 0 : -1);
nodVizitat[i] = false;
}
coada.push(s_bfs);
int nodCrt;///nodul curent din parcurgerea laterala
while(!coada.empty())
{
nodCrt = coada.front();
coada.pop();
nodVizitat[nodCrt] = true;
for (int i = 0; i < lsAd[nodCrt].size(); i++)///trec prin toate nodurile adiacente
{
if (!nodVizitat[lsAd[nodCrt][i]])///daca nodul este nevizitat il adaug in coada
{
distNod[lsAd[nodCrt][i]] = distNod[nodCrt]+1;
nodVizitat[lsAd[nodCrt][i]] = true;
coada.push(lsAd[nodCrt][i]);
}
}
}
///scrierea in fisier
ofstream out(fisierIesire);
for (int i = 1; i <= nrNoduri; i++)
out<<distNod[i]<<' ';
out.close();
delete[] distNod;
delete[] nodVizitat;
}
void Graf::CompBic(ofstream &out)
{
int nrComp = 0; ///variabila in care memorez numarul de componente biconexe gasite
set<int>* noduriComp = new set<int>[nrNoduri];///am mai multe multimi in care sunt nodurile fiecarei componente
int* parinte = new int[nrNoduri+1]();///array cu parintii fiecarui nod(pentru a verifica daca o muchie este intre tata si un fiu)
int* timpIntr = new int[nrNoduri+1]();///timpii de intrare in noduri
int* timpMin = new int[nrNoduri+1]();///timpul minim la care se poate ajunge(eventual printr-o muchie de intoarcere)
stack<pair<int,int>>* stiva = new stack<pair<int,int>>;///stiva folosita
///array-urile dinamice sunt deja initializate cu 0 pe toate pozitiile
for (int i = 1; i <= nrNoduri; i++)
{
if (timpIntr[i] == 0)///nu am vizitat nodul inca
{
CompBicTimp(i,nrComp,parinte,timpIntr,timpMin,stiva,noduriComp);
if (!stiva->empty()) ///dupa apelul functiei au ramas noduri(componenta nu contine muchie de intoarcere)
{
nrComp++;
while (!stiva->empty())
{
pair<int,int> m = stiva->top();
stiva->pop();
noduriComp[nrComp-1].insert(m.first);
noduriComp[nrComp-1].insert(m.second);
}
}
}
}
///partea de scriere in fisier
out<<nrComp<<'\n';
for (int i = 0; i < nrComp; i++)
{
for (auto itr : noduriComp[i])
out<<itr<<' ';
out<<'\n';
}
delete[] noduriComp;
delete[] parinte;
delete[] timpIntr;
delete[] timpMin;
delete stiva;
}
void Graf::CompBicTimp(int nodCrt, int &nrComp, int* parinte,int* timpIntr, int* timpMin, stack<pair<int,int>>* stiva, set<int>* noduriComp)
{
static int timp = 1;///variabila statica pentru calcularea timpilor de intrare
timpIntr[nodCrt] = timpMin[nodCrt] = timp++;
for (auto fiu : lsAd[nodCrt])
{
if (timpIntr[fiu] == 0)
{
parinte[fiu] = nodCrt;
stiva->push(make_pair(nodCrt,fiu));///adaugam muchia in stiva
CompBicTimp(fiu,nrComp,parinte,timpIntr,timpMin,stiva,noduriComp);
timpMin[nodCrt] = min(timpMin[nodCrt], timpMin[fiu]); ///actualizez timpul(nivelul) minim la care poate ajunge nodul curent
if (timpMin[fiu] >= timpIntr[nodCrt])///fiul nu poate ajunge la un stramos al lui nodCrt(deci avem o componenta)
{
nrComp++;
pair<int,int> p;
while (!(stiva->top().first == nodCrt && stiva->top().second == fiu))
{
p = stiva->top();
stiva->pop();
noduriComp[nrComp-1].insert(p.first);
noduriComp[nrComp-1].insert(p.second);
}
p = stiva->top();
stiva->pop();
noduriComp[nrComp-1].insert(p.first);
noduriComp[nrComp-1].insert(p.second);
}
}
else if (fiu != parinte[nodCrt])///am gasit o muchie de intoarcere
timpMin[nodCrt] = min(timpMin[nodCrt], timpIntr[fiu]);
}
}
void Graf::CTC(ofstream &out)
{
///asemanator cu biconex doar ca ma intereseaza doar nodurile si nu muchiile
int nrComp = 0;
int* parinte = new int[nrNoduri+1]();
int* timpIntr = new int[nrNoduri+1]();
int* timpMin = new int[nrNoduri+1]();
stack<int>* stiva = new stack<int>;
set<int>* noduriComp = new set<int>[nrNoduri];
bool* inStiva = new bool[nrNoduri+1]();///in plus fata de biconex, intr-o componenta tare conexa pot fi doar nodurile din dfs
for (int i = 1; i <= nrNoduri; i++)
if (timpIntr[i] == 0)///daca nodul este nevizitat incep dfs
CTCCalc(i,nrComp,timpIntr,timpMin,stiva,noduriComp,inStiva);
out<<nrComp<<'\n';
for (int i = 0; i < nrComp; i++)
{
for (auto itr : noduriComp[i])
out<<itr<<' ';
out<<'\n';
}
delete[] inStiva;
delete[] noduriComp;
delete stiva;
delete[] timpMin;
delete[] timpIntr;
delete[] parinte;
}
void Graf::CTCCalc(int nodCrt, int &nrComp,int* timpIntr, int* timpMin, stack<int>* stiva, set<int>* noduriComp, bool* inStiva)
{
static int timp = 1;
timpIntr[nodCrt] = timpMin[nodCrt] = timp++;
stiva->push(nodCrt);
inStiva[nodCrt] = true;
for (auto fiu : lsAd[nodCrt])
{
if (timpIntr[fiu] == 0) ///fiu nevizitat
{
cout<<fiu<<' '<<timpIntr[fiu]<<'\n';
inStiva[fiu] = true;
CTCCalc(fiu,nrComp,timpIntr,timpMin,stiva,noduriComp,inStiva);
timpMin[nodCrt] = min(timpMin[nodCrt], timpMin[fiu]);
}
else if (inStiva[fiu])///fiul este in stiva(muchia este de intoarcere)
timpMin[nodCrt] = min(timpMin[nodCrt], timpIntr[fiu]);
}
if (timpIntr[nodCrt] == timpMin[nodCrt])///nodul curent este radacina componentei tare conexe
{
nrComp++;
while (stiva->top() != nodCrt)///toate nodurile din stiva pana la nodul radacina fac parte din ctc
{
inStiva[stiva->top()] = false;
noduriComp[nrComp-1].insert(stiva->top());
stiva->pop();
}
inStiva[stiva->top()] = false;
noduriComp[nrComp-1].insert(stiva->top());
stiva->pop();
}
}
int main()
{
///pentru BFS
// Graf g;
// g.BFS();
///pentru DFS
// ifstream in("dfs.in");
// Graf g(false,in);
// g.DFS("dfs.out");
// in.close();
///pentru Componente Biconexe
// ifstream in("biconex.in");
// ofstream out("biconex.out");
// Graf g(false,in);
// in.close();
// g.CompBic(out);
// out.close();
///pentru Componente Tare Conexe
ifstream in("ctc.in");
Graf g(true, in);
ofstream out("ctc.out");
g.CTC(out);
out.close();
}