#include <iostream>
#include <fstream>
#include <queue>
#include <vector>
#include <stack>
#include <bits/stdc++.h>
using namespace std;
bool compara(int const a, int const b)
{
return (a > b);
}
class Graf
{
protected:
int numarNoduri, numarMuchii;
vector <vector <int>> listaAdiacenta;
public:
Graf(int const, int const, vector <vector<int>>);
Graf(const Graf &graf);
~Graf();
int get_noduri() const;
int get_muchii() const;
vector <vector<int>> get_lista() const;
void afisare(ostream&);
friend ostream& operator<<(ostream& out, Graf &g);
vector <int> BFS(int);
int DFS_componente_conexe();
vector <int> sortareTopologica(int []);
int existaGrafHH(vector <int> &, int);
void ctc(vector <int>, stack <int>, vector <vector<int>>, vector <int>, ostream &);
void DfsMuchiiCritice(int, int, vector <int> &, vector <int> &, vector <int> &, vector <vector<int>> &, ostream &);
int Find(int, vector <int> &);
void Union(int, int, vector <int> &, vector <int> &);
private:
void DFS(int, vector <int> &);
void dfs1(int, vector <int> &, stack <int>&);
void dfs2(int, vector <int> &, vector <vector<int>>, vector <vector<int>> &, vector <int> &);
};
Graf :: Graf(int const numarNodurioduri, int const numarMuchiiuchii, vector <vector<int>> lista)
{
numarNoduri = numarNodurioduri;
numarMuchii = numarMuchiiuchii;
listaAdiacenta = lista;
}
Graf :: Graf(const Graf & graf)
{
numarNoduri = graf.numarNoduri;
numarMuchii = graf.numarMuchii;
listaAdiacenta = graf.listaAdiacenta;
}
Graf :: ~Graf()
{
numarNoduri = 0;
numarMuchii = 0;
listaAdiacenta.clear();
}
int Graf :: get_noduri() const
{
return numarNoduri;
}
int Graf :: get_muchii() const
{
return numarMuchii;
}
vector <vector<int>> Graf :: get_lista() const
{
return listaAdiacenta;
}
void Graf :: afisare(ostream &out)
{
out << "Numar de noduri: " << numarNoduri << "\n";
out << "Numar de muchii: " << numarMuchii << "\n";
for (int i = 0; i < numarNoduri; i++)
for (int j = 0; j < listaAdiacenta[i].size(); j++)
{
out << "Muchia " << i << " " << listaAdiacenta[i][j];
out << "\n";
}
}
ostream& operator<<(ostream& out, Graf &g)
{
g.afisare(out);
return out;
}
vector <int> Graf :: BFS(int start)
{
vector <int> cost(numarNoduri + 1, -1);
int S;
queue <int> coada;
coada.push(start);
cost[start] = 0;
while (!coada.empty())
{
S = coada.front();
for (int i = 0; i < listaAdiacenta[S].size(); i++)
{
int nod_curent = listaAdiacenta[S][i];
if (cost[nod_curent] == -1)
{
cost[nod_curent] = cost[S] + 1;
coada.push(nod_curent);
}
}
coada.pop();
}
return cost;
}
void Graf :: DFS(int nod, vector <int> &viz)
{
viz[nod] = 1;
for (int i = 0; i < listaAdiacenta[nod].size(); i++)
{
int nod_curent = listaAdiacenta[nod][i];
if (viz[nod_curent] == 0)
DFS(nod_curent, viz);
}
}
int Graf :: DFS_componente_conexe()
{
vector <int> viz(numarNoduri + 1, 0);
int nrCC = 0;
for (int i = 1; i <= numarNoduri; i++)
{
if (viz[i] == 0)
{
DFS(i, viz);
nrCC ++;
}
}
return nrCC;
}
vector <int> Graf :: sortareTopologica(int grade[])
{
queue <int> coada;
vector <int> rez;
for (int i = 1; i <= numarNoduri; i++)
if (grade[i] == 0)
coada.push(i);
while (!coada.empty())
{
int nod_curent = coada.front();
rez.push_back(nod_curent);
coada.pop();
for (int i = 0; i < listaAdiacenta[nod_curent].size(); i++)
{
int vecin = listaAdiacenta[nod_curent][i];
grade[vecin]--;
if (grade[vecin] == 0)
coada.push(vecin);
}
}
return rez;
}
int Graf :: existaGrafHH(vector <int> &v, int n)
{
int ok = 1;
while (ok == 1)
{
sort(v.begin(), v.end(), compara);
if (v[0] == 0)
break;
int nr = v[0];
v.erase(v.begin());
if (nr > v.size())
{
ok = 0;
break;
}
for (int i = 0; i < nr; i++)
{
v[i] = v[i] - 1;
if (v[i] < 0)
{
ok = 0;
break;
}
}
}
if (ok == 1) return 1;
return 0;
}
void Graf :: dfs1(int nod, vector <int> &vizitat, stack <int> &stiva)
{
vizitat[nod] = 1;
for (int i = 0; i < listaAdiacenta[nod].size(); i++)
{
int nod_curent = listaAdiacenta[nod][i];
if (vizitat[nod_curent] == 0)
dfs1(nod_curent, vizitat, stiva);
}
stiva.push(nod);
}
void Graf :: dfs2(int nod, vector <int> &vizitatTr, vector <vector<int>> listaTr, vector <vector<int>> &rez, vector <int> &vector_aux)
{
vector_aux.push_back(nod);
vizitatTr[nod] = 1;
for (int i = 0; i < listaTr[nod].size(); i++)
{
int nod_curent = listaTr[nod][i];
if (vizitatTr[nod_curent] == 0)
dfs2(nod_curent, vizitatTr, listaTr, rez, vector_aux);
}
}
void Graf :: ctc(vector <int> vizitat, stack <int> stiva, vector <vector<int>> listaTr, vector <int> vizitatTr, ostream &out)
{
vector <vector<int>> rez(numarNoduri + 1);
int nr = 0;
for (int i = 1; i <= numarNoduri; i++)
if (vizitat[i] == 0)
dfs1(i, vizitat, stiva);
while (!stiva.empty())
{
int v = stiva.top();
stiva.pop();
if (vizitatTr[v] == 0)
{
vector <int> vector_aux;
dfs2(v, vizitatTr, listaTr, rez, vector_aux);
rez.push_back(vector_aux);
nr++;
}
}
out << nr << "\n";
for (int i = 0; i < rez.size(); i++)
{
if (rez[i].size() != 0)
{
for (int j = 0; j < rez[i].size(); j++)
out << rez[i][j] << " ";
out << "\n";
}
}
}
void Graf :: DfsMuchiiCritice(int nod, int tata, vector <int> &vizitat, vector <int> &nivel, vector <int> &nivelInt, vector <vector<int>> &listaAdiacenta,
ostream &out)
{
vizitat[nod] = 1;
nivel[nod] = nivel[tata] + 1;
nivelInt[nod] = nivel[nod];
for (int i = 0; i < listaAdiacenta[nod].size(); i++)
{
int copil = listaAdiacenta[nod][i];
if (copil != tata)
{
if (vizitat[copil] == 1)
{
if (nivelInt[nod] > nivel[copil])
nivelInt[nod] = nivel[copil];
}
else
{
DfsMuchiiCritice(copil, nod, vizitat, nivel, nivelInt, listaAdiacenta, out);
if (nivelInt[nod] > nivelInt[copil])
nivelInt[nod] = nivelInt[copil];
if (nivel[nod] < nivelInt[copil])
out << nod << " " << copil << "\n";
}
}
}
}
int Graf :: Find(int nod, vector <int> &tata)
{
if (tata[nod] == nod)
return nod;
int rez = Find(tata[nod], tata);
tata[nod] = rez;
return rez;
}
void Graf :: Union(int nod1, int nod2, vector <int> &tata, vector <int> &rang)
{
int TataNod1 = Find(nod1, tata);
int TataNod2 = Find(nod2, tata);
if (TataNod1 == TataNod2)
return;
if (rang[TataNod1] < rang[TataNod2])
tata[TataNod1] = TataNod2;
else if (rang[TataNod1] > rang[TataNod2])
tata[TataNod2] = TataNod1;
else
{
tata[TataNod2] = TataNod1;
rang[TataNod1]++;
}
}
bool comparaCost (vector <int> v1, vector <int> v2)
{
return v1[2] < v2[2];
}
class GrafPonderat : public Graf
{
private:
vector <vector <pair<int, int>>> muchiiCuCost;
int find_tata(int, vector <int>);
void set_tata(int, int, vector <int> &);
public:
GrafPonderat(int, int, vector <vector <int>>, vector <vector<pair <int, int>>>);
vector <pair<int, int>> apmKruskal(ostream &, vector <int>, vector <vector <int>>);
vector <int> dijkstra();
void bellmanford(ostream &);
};
GrafPonderat :: GrafPonderat(int numarNodurioduri, int numarMuchiiuchii, vector <vector <int>> lista, vector <vector<pair<int, int>>> muchiiCost) : Graf(numarNodurioduri, numarMuchiiuchii, lista) ///apelam constructorul din baza
{
muchiiCuCost = muchiiCost;
}
int GrafPonderat :: find_tata(int nod, vector <int> tata)
{
while (nod != tata[nod])
nod = tata[nod];
return nod;
}
void GrafPonderat :: set_tata(int nod1, int nod2, vector <int> &tata)
{
tata[nod1] = tata[nod2];
}
vector <pair<int, int>> GrafPonderat :: apmKruskal(ostream &out, vector <int> tata, vector <vector <int>> muchiiCost)
{
vector <pair<int, int>> rez;
sort (muchiiCost.begin(), muchiiCost.end(), comparaCost);
int cost_total = 0, nod1, nod2, tata1, tata2, numarMuchiiuchiiAPM = 0, index_muchie = 0;
while (numarMuchiiuchiiAPM < numarNoduri - 1)
{
nod1 = muchiiCost[index_muchie][0];
nod2 = muchiiCost[index_muchie][1];
tata1 = find_tata(nod1, tata);
tata2 = find_tata(nod2, tata);
if (tata1 != tata2)
{
if (tata1 < tata2)
set_tata(tata1, tata2, tata);
else
set_tata(tata2, tata1, tata);
rez.push_back(make_pair(nod1, nod2));
cost_total = cost_total + muchiiCost[index_muchie][2];
numarMuchiiuchiiAPM++;
}
index_muchie++;
}
out << cost_total << "\n";
out << numarMuchiiuchiiAPM << "\n";
return rez;
}
vector <int> GrafPonderat :: dijkstra()
{
vector <int> distante(numarNoduri + 1, INT_MAX);
vector <int> inCoada(numarNoduri + 1, 0);
priority_queue <pair <int, int>, vector <pair <int, int>>, greater<pair <int, int>>> coada;
distante[1] = 0;
coada.push({0, 1});
inCoada[1] = 1;
while (!coada.empty())
{
int nodCurent = coada.top().second;
coada.pop();
inCoada[nodCurent] = 0;
for (int i = 0; i < muchiiCuCost[nodCurent].size(); i++)
{
int vecin, cost_vecin;
vecin = muchiiCuCost[nodCurent][i].first;
cost_vecin = muchiiCuCost[nodCurent][i].second;
if (distante[nodCurent] + cost_vecin < distante[vecin])
{
distante[vecin] = distante[nodCurent] + cost_vecin;
if (inCoada[vecin] == 0)
{
coada.push({distante[vecin], vecin});
inCoada[vecin] = 1;
}
}
}
}
return distante;
}
void GrafPonderat :: bellmanford(ostream &out)
{
vector <int> distante(numarNoduri + 1, INT_MAX);
vector <int> inCoada(numarNoduri + 1, 0);
vector <int> vizitat(numarNoduri + 1, 0);
priority_queue <pair <int, int>, vector <pair <int, int>>, greater<pair <int, int>>> coada;
int ok = 1;
distante[1] = 0;
coada.push({0, 1});
inCoada[1] = 1;
while (!coada.empty())
{
int nodCurent = coada.top().second;
coada.pop();
inCoada[nodCurent] = 0;
vizitat[nodCurent]++;
if (vizitat[nodCurent] > numarNoduri - 1)
{
ok = 0;
break;
}
for (int i = 0; i < muchiiCuCost[nodCurent].size(); i++)
{
int vecin, cost_vecin;
vecin = muchiiCuCost[nodCurent][i].first;
cost_vecin = muchiiCuCost[nodCurent][i].second;
if (distante[nodCurent] + cost_vecin < distante[vecin])
{
distante[vecin] = distante[nodCurent] + cost_vecin;
if (inCoada[vecin] == 0)
{
coada.push({distante[vecin], vecin});
inCoada[vecin] = 1;
}
}
}
}
if (ok == 0)
out << "Ciclu negativ!";
else
for (int i = 2; i < distante.size(); i++)
out << distante[i] << " ";
}
void problemaBFS()
{
int n, s, st, dr, m;
ifstream fin("bfs.in");
ofstream fout("bfs.out");
fin >> n >> m >> s;
vector <vector<int>> listaAdiacenta(n + 1);
for (int i = 1; i <= m; i++)
{
// cin >> st >> dr;
fin >> st >> dr;
listaAdiacenta[st].push_back(dr);
}
Graf g(n, m, listaAdiacenta);
vector <int> cost = g.BFS(s);
for (int i = 1; i <= n; i++)
fout << cost[i] << " ";
fin.close();
fout.close();
}
void problemaDFS()
{
int n, m, st, dr;
ifstream fin("dfs.in");
ofstream fout("dfs.out");
fin >> n >> m;
vector <vector<int>> listaAdiacenta(n + 1);
for (int i = 1; i <= m; i++)
{
// cin >> st >> dr;
fin >> st >> dr;
listaAdiacenta[st].push_back(dr);
listaAdiacenta[dr].push_back(st);
}
Graf g(n, m, listaAdiacenta);
int rez = g.DFS_componente_conexe();
fout << rez;
fin.close();
fout.close();
}
void problemaHavelHakimi()
{
vector <int> v;
int n, el;
cout << "Introduceti numarul de elemente: ";
cin >> n;
cout << "Introduceti gradele: ";
for (int i = 0; i < n; i++)
{
cin >> el;
v.push_back(el);
}
vector <vector<int>> vgol = {};
Graf g(0, 0, vgol);
int rez = g.existaGrafHH(v, n);
if (rez == 1)
cout << "Da, exista graf!";
else cout << "Nu, nu exista graf!";
}
void problemaSortareTopologica()
{
int n, m, st, dr, grade[50001] = {0};
ifstream fin("sortaret.in");
ofstream fout("sortaret.out");
fin >> n >> m;
vector <vector<int>> listaAdiacenta (n + 1);
for (int i = 1; i <= m; i++)
{
// cin >> st >> dr;
fin >> st >> dr;
listaAdiacenta[st].push_back(dr);
grade[dr]++;
}
Graf g(n, m, listaAdiacenta);
vector <int> rez = g.sortareTopologica(grade);
for (int i=0; i < rez.size(); i++)
fout << rez[i] << " ";
fin.close();
fout.close();
}
void problemaComponenteTareConexe()
{
int n, m, st, dr;
ifstream fin("ctc.in");
ofstream fout("ctc.out");
fin >> n >> m;
vector <vector<int>> listaTr(n + 1);
vector <vector<int>> listaAdiacenta(n + 1);
vector <int> vizitat(n + 1, 0);
vector <int> vizitatTr(n + 1, 0);
stack <int> stiva;
for (int i = 1; i <= m; i++)
{
fin >> st >> dr;
listaAdiacenta[st].push_back(dr);
listaTr[dr].push_back(st);
}
Graf g(n, m, listaAdiacenta);
g.ctc(vizitat, stiva, listaTr, vizitatTr, fout);
fin.close();
fout.close();
}
void problemaCriticalConnectionsLeetCode()
{
int n, m, st, dr;
cout << "Numerotare noduri de la index 0! Graf neorientat!" << "\n";
cout << "Introduceti numarul de noduri: ";
cin >> n;
cout << "Introduceti numarul de muchii: ";
cin >> m;
cout << "Introduceti muchiile: ";
vector <vector<int>> listaAdiacenta(n + 1);
vector <int> vizitat(n + 1, 0);
vector <int> nivel(n + 1, 0);
vector <int>nivelInt(n + 1, 0);
for (int i = 1; i <= m; i++)
{
cin >> st >> dr;
listaAdiacenta[st].push_back(dr);
listaAdiacenta[dr].push_back(st);
}
Graf g(n, m, listaAdiacenta);
cout << "Muchii critice: ";
g.DfsMuchiiCritice(0, 0, vizitat, nivel, nivelInt, listaAdiacenta, cout);
}
void problemaPaduriDeMultimiDisjuncte()
{
ifstream fin("disjoint.in");
ofstream fout("disjoint.out");
int n, m, st, dr, operatie;
fin >> n >> m;
vector <vector<int>> vgol = {};
vector <int> tata(n + 1);
vector <int> rang(n + 1, 0);
Graf g(n, m, vgol);
for (int i = 1; i <= n; i++)
tata[i] = i;
for (int i = 1; i <= m; i++)
{
fin >> operatie >> st >> dr;
if (operatie == 1)
{
g.Union(st, dr, tata, rang);
}
else if (g.Find(st, tata) == g.Find(dr, tata))
fout << "DA" << "\n";
else fout << "NU" << "\n";
}
fin.close();
fout.close();
}
void problemaAPMKruskal()
{
ifstream fin("apm.in");
ofstream fout("apm.out");
int n, m, st, dr, cost;
fin >> n >> m;
vector <vector <int>> lista = {};
vector <vector<pair <int, int>>> muchiiCuCost(n + 1, {{0,0}});
vector <vector <int>> muchiiCost;
for (int i = 1; i <= m; i++)
{
fin >> st >> dr >> cost;
muchiiCost.push_back({st, dr, cost});
}
GrafPonderat g(n, m, lista, muchiiCuCost);
vector <int> tata(n + 1);
for (int i = 1; i <= n; i++)
tata[i] = i;
vector <pair<int, int>> rezultat = g.apmKruskal(fout, tata, muchiiCost);
for (int i = 0; i < rezultat.size(); i++)
fout << rezultat[i].first << " " << rezultat[i].second << "\n";
fin.close();
fout.close();
}
void problemaDijkstra()
{
ifstream fin("dijkstra.in");
ofstream fout("dijkstra.out");
int n, m, st, dr, cost;
fin >> n >> m;
vector <vector <int>> lista = {};
vector <vector<pair <int, int>>> muchiiCuCost(n + 1);
for (int i = 1; i <= m; i++)
{
fin >> st >> dr >> cost;
muchiiCuCost[st].push_back(make_pair(dr, cost));
}
GrafPonderat g(n, m, lista, muchiiCuCost);
vector <int> rez = g.dijkstra();
for (int i = 2; i < rez.size(); i++)
if (rez[i] == INT_MAX)
fout << 0 << " ";
else
fout << rez[i] << " ";
fin.close();
fout.close();
}
void problemaBellmanford()
{
ifstream fin("bellmanford.in");
ofstream fout("bellmanford.out");
int n, m, st, dr, cost;
fin >> n >> m;
vector <vector <int>> lista = {};
vector <vector<pair <int, int>>> muchiiCuCost(n + 1);
for (int i = 1; i <= m; i++)
{
fin >> st >> dr >> cost;
muchiiCuCost[st].push_back(make_pair(dr, cost));
}
GrafPonderat g(n, m, lista, muchiiCuCost);
g.bellmanford(fout);
fin.close();
fout.close();
}
int main()
{
problemaAPMKruskal();
return 0;
}