#include <bits/stdc++.h>
using namespace std;
class graf
{
protected:
int n;
int m;
static const int nmax=101;
///structuri de retinere a muchiilor
vector<vector<int>> muchii; //liste de adiacenta- array de vectori
vector< tuple<int,int,int> > muchii_costuri_lista; //array de muchii cu costuri (e1,e2,cost)
vector<vector< pair<int,int> >> muchii_costuri_adiacenta; //liste de adiacenta- array de vectori pentru muchii cu costuri (nod,cost)
vector<vector<int>> matrice_ponderi;
protected:
int Reprezentant(int x, vector<int>& rad);
public:
graf(int noduri, int muchii):n(noduri), m(muchii) {}
void Citire_muchii_costuri_lista(ifstream& fin);
static bool HavelHakimi(vector<int>& grade);
//pentru paduri de multimi disjuncte
void Reuneste(int x, int y, vector<int>& h, vector<int>& rad);
bool Verifica(int x, int y, vector<int>& rad);
int Comp_conexe();
vector<int> BFS(int start); //returneaza distantele de la start la fiecare nod
void DFS(int start, vector<vector<int>>& muchii_curente, int viz[]);
vector<int> Dijkstra(int s);
};
class graf_orientat: public graf
{
private:
void DFStimpi(int nod,vector<vector<int>>& muchii_curente, int viz[], stack<int>& timpi_final); //memoreaza si timpii de final
void DFSctc (int nod, vector<vector<int>>& muchii_curente, int viz[], int nr_comp, vector<vector<int>>& comp_tare_con);//memoreaza si comp tare conexe
vector<vector<int>> Graf_transpus();
bool Exista_lant_nesaturat(int s,int d, vector<int>& tata, vector<vector<int>>& muchii,
vector<vector<int>>& c, vector<vector<int>>& f, vector<int>& viz);
public:
graf_orientat(int noduri, int muchii):graf(noduri,muchii) {}
void Citire_muchii_adiacenta(ifstream& fin);
void Citire_muchii_costuri_adiacenta(ifstream& fin);
void Citire_matrice_ponderi(ifstream& fin);
pair<vector<vector<int>>, int> CTC(); //alg. lui Kosaraju
vector<int> SortareTopologica();
vector<int> BellmanFord(int s);
vector<vector<int>> RoyFloyd();
int Max_flow(int s, int d);
};
class graf_neorientat: public graf
{
private:
void DFScritice(int nod, vector<vector<int>>& muchii, int viz[], vector<pair<int,int>>& muchii_critice, int nivel[], int nivel_min[] );
void DFSbiconex(int nod, vector<vector<int>>& muchii, int viz[], int nivel[], int nivel_min[], stack<int>& noduri_traversate, vector<vector<int>>& componente);
void DFSdiametru(int nod, vector<vector<int>>& muchii, int viz[], int distante[]);
public:
graf_neorientat(int noduri, int muchii):graf(noduri,muchii) {}
void Citire_muchii_adiacenta(ifstream& fin);
void Citire_muchii_costuri_adiacenta(ifstream& fin);
vector<pair<int,int>> MuchiiCritice();
vector<vector<int>> Componente_biconexe();
vector<tuple<int,int,int>> Apm(); //alg. lui Kruskal
int Diametru_arbore();
};
vector<vector<int>> graf_orientat::RoyFloyd()
{
///complexitate: O(n^3)
vector<vector<int>> drumuri_minime;
const int valmax=100000000; //folosita la initializare in locurile unde nu exista muchie pentru a nu folosi costul 0
drumuri_minime.resize(n);
for(int i=0; i<n; ++i)
for(int j=0; j<n; ++j)
if(matrice_ponderi[i][j])
drumuri_minime[i].push_back(matrice_ponderi[i][j]);
else
drumuri_minime[i].push_back(valmax);
//pentru orice pereche de noduri (i,j) incerc sa optimizez drumul folosind un intermediar k
for(int k=0; k<n; ++k)
for(int i=0; i<n; ++i)
for(int j=0; j<n; ++j)
if(drumuri_minime[i][j]>drumuri_minime[i][k]+drumuri_minime[k][j])
drumuri_minime[i][j]=drumuri_minime[i][k]+drumuri_minime[k][j];
return drumuri_minime;
}
void graf_neorientat::DFSdiametru(int nod, vector<vector<int>>& muchii_curente, int viz[], int dist[])
{
///complexitate: O(n+m)
viz[nod]=1;
for(int i=0; i<(int)muchii_curente[nod].size(); ++i)
if(!viz[muchii_curente[nod][i]])
{
dist[muchii_curente[nod][i]]=dist[nod]+1;
DFSdiametru(muchii_curente[nod][i], muchii_curente, viz,dist);
}
}
int graf_neorientat::Diametru_arbore()
{
///complexitate: 3*DFS cu m=n-1 => O(n)
int dist[n+1];
int viz[n+1];
int maxim=-1, nod=1;
for(int i=1; i<=n; ++i)
viz[i]=0;
//incep din nodul 1 (un nod random) sa fac dfs pentru a gasi cea mai indepartata frunza
dist[1]=1;
DFSdiametru(1, muchii, viz, dist);
for(int i=1; i<=n; ++i)
if(dist[i]>maxim)
maxim=dist[i], nod=i;
//continui cu un dfs din nodul cel mai indepartat gasit pentru a gasi celalalt capat al lantului maxim
for(int i=1; i<=n; ++i)
viz[i]=0;
dist[nod]=1;
DFSdiametru(nod, muchii, viz, dist);
maxim=-1;
for(int i=1; i<=n; ++i)
if(dist[i]>maxim)
maxim=dist[i];
return maxim;
}
vector<int> graf_orientat::BellmanFord(int s)
{
///complexitate: O(n*m), cu optimizare cu coada (Shortest Path Faster Algorithm)
//folosind muchii_costuri_adiacenta
bool in_coada[n+1];
bool ok=1; //1-nu are cicluri negative, 0-are
vector <int> d;
int nr_incr[n+1]; //retine pentru fiecare nod numarul de incrementari pentru a detecta cicluri negative
const int inf=250000005; //m*costmax muchie
queue<int> noduri_modificate; //nodurile pentru care are sens sa incerc relaxarea vecinilor
//(distantele acestora s-au modificat la pasul anterior)
d.resize(n+1);
for(int i=1; i<=n; ++i)
{
nr_incr[i]=0;
in_coada[i]=0;
if(i!=s)
d[i]=inf;
else d[i]=0;
}
noduri_modificate.push(s); //consider sursa ca fiind importanta la inceput
in_coada[s]=1;
//iau fiecare muchie care porneste dintr-un nod care s-a modificat anterior
while(!noduri_modificate.empty() && ok)
{
int nod1=noduri_modificate.front();
noduri_modificate.pop();
in_coada[nod1]=0;
for(int i=0; i<(int)muchii_costuri_adiacenta[nod1].size(); ++i)
{
int nod2=get<0>(muchii_costuri_adiacenta[nod1][i]);
int cost=get<1>(muchii_costuri_adiacenta[nod1][i]);
//am acum muchia nod1-nod2 cu costul lor
if(d[nod2] > d[nod1]+cost) //pot relaxa muchia
{
d[nod2]=d[nod1]+cost;
nr_incr[nod2]++;
if(!in_coada[nod2]) //daca nu era in coada nodurilor importante (care s-au actualizat), il adaug
{
noduri_modificate.push(nod2);
in_coada[nod2]=1;
}
if(nr_incr[nod2]>=n) //apare ciclu negativ pentru ca sunt prea multe incrementari (bucla infinita)
{
ok=0;
d.clear();
break;
}
}
}
}
return d;
}
vector<int> graf::Dijkstra(int s)
{
///complexitate: O(m * logn)
//folosind muchii_costuri_adiacenta
bool viz[n+1]; //pentru a marca nodurile prin care trecem
vector<int> d; //distanta de la s la celelalte noduri
const int inf = 250005;
//min heap cu sortare dupa distanta (primul element)
priority_queue <pair<int,int>, vector<pair<int,int>>, greater<pair<int, int>> >dist_nod;
//initializarea
d.resize(n+1);
for(int i=1; i<=n; ++i)
{
viz[i]=0;
if(i!=s)
d[i]=inf;
else
{
d[i]=0;
dist_nod.push(make_pair(d[i],i)); //adaug doar nodul sursa in heap initial
}
}
while (!dist_nod.empty())
{
int u=get<1>(dist_nod.top()); //extrage nodul cu eticheta minima (distanta minima)
dist_nod.pop();
if(!viz[u]) //verific sa nu mai fi trecut prin el
{
viz[u]=1;
for(int i=0; i<(int)muchii_costuri_adiacenta[u].size(); ++i) //caut nodurile v pentru care exista muchia uv
{
int v,cost;
v=get<0>(muchii_costuri_adiacenta[u][i]);
cost=get<1>(muchii_costuri_adiacenta[u][i]); //dintre u si v
//verific daca pot relaxa muchia
if(d[v] > d[u] + cost)
{
d[v]=d[u]+cost; //actualizez distanta
dist_nod.push(make_pair(d[v],v)); //bag in heap doar daca nu era deja ca sa nu am dubluri
}
}
}
}
return d;
}
void graf_neorientat::DFSbiconex(int nod,vector<vector<int>>& muchii, int viz[], int nivel[], int nivel_min[], stack<int>& noduri_traversate, vector<vector<int> >& componente)
{
int fiu;
viz[nod]=1;
noduri_traversate.push(nod);
nivel_min[nod]=nivel[nod];
for(int j=0; j<(int)muchii[nod].size(); ++j)
{
fiu=muchii[nod][j];
if(!viz[fiu])
{
nivel[fiu]=nivel[nod]+1;
DFSbiconex(fiu, muchii, viz, nivel,nivel_min,noduri_traversate,componente);
//formula A de la muchii critice
//actualizez nivelul minim al nodului curent deoarece fiul poate urca la un nod de deasupra celui curent
nivel_min[nod]=min(nivel_min[nod], nivel_min[fiu]);
//verific daca parintele e nod critic
if(nivel_min[fiu]>=nivel[nod])
{
vector<int> temp;
//elimin din stiva pana la fiu, apoi fiul si nodul curent
//nu se elimina pana la nodul curent direct deoarece se mai pot afla noduri
//de la alta componenta biconexa intre cel curent si fiu
while(noduri_traversate.top()!=fiu)
{
temp.push_back(noduri_traversate.top());
noduri_traversate.pop();
}
temp.push_back(fiu);
noduri_traversate.pop();
temp.push_back(nod);
//nu elimin nodul tata deoarece el poate face parte si din alta componenta biconexa
componente.push_back(temp);
}
}
else //muchie de intoarcere
if(nivel[fiu]<nivel[nod]-1)
//formula B de la muchii critice
nivel_min[nod]=min(nivel_min[nod], nivel[fiu]);
}
}
vector<vector<int>> graf_neorientat::Componente_biconexe()
{
///asemanator cu muchiile critice
///complexitate: O(n+m)
vector<vector<int>> componente;
stack<int> noduri_traversate;
int nivel[n+1];
int nivel_min[n+1];
int viz[n+1];
for(int i=1; i<=n; ++i)
viz[i]=0;
nivel[1]=1;
DFSbiconex(1,muchii,viz,nivel,nivel_min,noduri_traversate,componente);
return componente;
}
void graf_neorientat::DFScritice(int nod, vector<vector<int>>& muchii,int viz[], vector<pair<int,int>>& muchii_critice, int nivel[], int nivel_min[])
{
int fiu;
viz[nod]=1;
//setez nivelul minim ca fiind cel curent
nivel_min[nod]=nivel[nod];
for(int j=0; j<(int)muchii[nod].size(); ++j)
{
fiu=muchii[nod][j];
if(!viz[fiu])
{
nivel[fiu]=nivel[nod]+1; //actualizez nivelul fiului
DFScritice(fiu, muchii,viz, muchii_critice,nivel,nivel_min);
//la intoarcerea din dfs, actualizez dupa formula A (curs)
nivel_min[nod]=min(nivel_min[nod], nivel_min[fiu]);
//verific daca e muchie critica
if(nivel_min[fiu]>nivel[nod])
muchii_critice.push_back(make_pair(nod,fiu));
}
else //muchie de intoarcere
if(nivel[fiu]<nivel[nod])
//formula B (curs)
//actualizez nivelul minim al nodului curent deoarece
//fiul este deasupra celui curent
nivel_min[nod]=min(nivel_min[nod], nivel[fiu]);
}
}
vector<pair<int,int>> graf_neorientat::MuchiiCritice()
{
///complexitate: O(n+m)
//pentru fiecare nod calculez nivelul si nivelul minim la care poate ajunge
//actualizez nivelul minim in functie de caz si verific daca gasesc muchie critica
int nivel[n+1];
int nivel_min[n+1];
vector<pair<int,int>> muchii_critice;
int viz[n+1];
for(int i=1; i<=n; ++i)
viz[i]=0;
nivel[1]=1;
DFScritice(1, muchii, viz, muchii_critice, nivel, nivel_min);
return muchii_critice;
}
vector<int> graf_orientat::SortareTopologica()
{
///complexitate O(n+m)
int grad_intern[n], vf;
queue<int> sortare;
vector<int> sortare_top;
for(int i=1; i<=n; i++)
grad_intern[i]=0;
//calculez gradele interne pentru noduri
for(int i=1; i<=n; ++i)
for(int j=0; j<(int)muchii[i].size(); ++j)
grad_intern[muchii[i][j]]+=1;
//adaug toate nodurile cu grad intern 0 intr-o coada
for(int i=1; i<=n; ++i)
if(grad_intern[i]==0)
sortare.push(i);
while(!sortare.empty())
{
//extrag primul element din coada
vf=sortare.front();
sortare.pop();
sortare_top.push_back(vf);
//scad gradele vecinilor (elimin nodul curent in mod fictiv)
//adaug in coada nodurile cu noul grad intern 0
for(int j=0; j<(int)muchii[vf].size(); ++j)
{
grad_intern[muchii[vf][j]]--;
if(grad_intern[muchii[vf][j]]==0)
sortare.push(muchii[vf][j]);
}
}
return sortare_top;
}
vector<vector<int>> graf_orientat::Graf_transpus()
{
vector<vector<int>> muchii_transp;
muchii_transp.resize(n+1);
for(int i=1; i<=n; ++i)
for(int j=0; j<(int)muchii[i].size(); ++j)
muchii_transp[muchii[i][j]].push_back(i);
return muchii_transp;
}
void graf_orientat::DFStimpi(int nod, vector<vector<int>>& muchii_curente, int viz[], stack<int> & timpi_final)
{
viz[nod]=1;
for(int i=0; i<(int)muchii_curente[nod].size(); ++i)
if(!viz[muchii_curente[nod][i]])
DFStimpi(muchii_curente[nod][i], muchii_curente, viz, timpi_final);
timpi_final.push(nod);//am terminat cu un nod, il adaug in stiva
}
void graf_orientat::DFSctc (int nod, vector<vector<int>>& muchii_curente, int viz[], int nr_comp, vector<vector<int>>& comp_tare_con)
{
viz[nod]=1;
//adaug nodul la componenta tare conexa respectiva
comp_tare_con[nr_comp].push_back(nod);
for(int i=0; i<(int)muchii_curente[nod].size(); ++i)
if(!viz[muchii_curente[nod][i]])
DFSctc(muchii_curente[nod][i], muchii_curente, viz, nr_comp, comp_tare_con);
}
pair<vector<vector<int>>, int> graf_orientat::CTC()
{
/// alg. lui Kosaraju
///complexitate: O(n+m)
//returneaza componentele tare conexe si numarul lor
int nr=0; //numarul de ctc
stack<int> timpi_final;
vector<vector<int>> muchii_transp; //liste de adiacenta- array de vectori
vector<vector<int>> comp_tare_con; //ctc, poz1- elem comp1, poz2-elem comp2 etc.
comp_tare_con.resize(n+1);
int viz[n+1];
for(int i=1; i<=n; ++i)
viz[i]=0;
//pas1: creez graful transpus (muchii inversate)
muchii_transp=Graf_transpus();
//pas2: dfs in care retin timpii de final(stiva) pe graful initial
for(int i=1; i<=n; ++i)
if(!viz[i])
DFStimpi(i, muchii, viz, timpi_final);
for(int i=1; i<=n; ++i)
viz[i]=0;
//pas3:dfs in functie de timpii de final pe graful transpus
while(!timpi_final.empty())
{
int vf=timpi_final.top();
timpi_final.pop();
if(!viz[vf])
{
nr++;
DFSctc(vf, muchii_transp, viz, nr, comp_tare_con);
}
}
return make_pair(comp_tare_con,nr);
}
int graf::Comp_conexe()
{
int viz[n+1];
for(int i=1; i<=n; ++i)
viz[i]=0;
int nr_comp=0;
for(int i=1; i<=n; ++i)
if(!viz[i])
{
DFS(i, muchii, viz);
nr_comp++;
}
return nr_comp;
}
void graf::DFS(int nod, vector<vector<int>>& muchii_curente, int viz[])
{
///complexitate: O(n+m)
viz[nod]=1;
for(int i=0; i<(int)muchii_curente[nod].size(); ++i)
if(!viz[muchii_curente[nod][i]])
DFS(muchii_curente[nod][i], muchii_curente, viz);
}
vector<int> graf::BFS(int start)
{
///complexitate: O(n+m)
queue<int> coada;
vector<int> viz;
vector<int> dist;
viz.resize(n+1);
dist.resize(n+1);
for(int i=1; i<=n; ++i)
dist[i]=-1, viz[i]=0;
viz[start]=1;
dist[start]=0;
coada.push(start);
while(!coada.empty())
{
int vf=coada.front();
coada.pop();
for(int i=0; i<(int)muchii[vf].size(); ++i)
if(!viz[muchii[vf][i]])
{
viz[muchii[vf][i]]=1;
dist[muchii[vf][i]]=dist[vf]+1;
coada.push(muchii[vf][i]);
}
}
return dist;
}
void graf_orientat::Citire_matrice_ponderi(ifstream& fin)
{
int muchii=0,x;
matrice_ponderi.resize(n);
for(int i=0; i<n; ++i)
for(int j=0; j<n; ++j)
{
fin>>x;
matrice_ponderi[i].push_back(x);
if(matrice_ponderi[i][j])
muchii++;
}
m=muchii;
}
void graf_orientat::Citire_muchii_adiacenta(ifstream& fin)
{
int n1,n2;
muchii.resize(n+1);
for(int i=0; i<m; ++i)
{
fin>>n1>>n2;
muchii[n1].push_back(n2);
}
}
void graf_neorientat::Citire_muchii_adiacenta(ifstream& fin)
{
int n1,n2;
muchii.resize(n+1);
for(int i=0; i<m; ++i)
{
fin>>n1>>n2;
muchii[n1].push_back(n2);
muchii[n2].push_back(n1);
}
}
void graf_orientat::Citire_muchii_costuri_adiacenta(ifstream& fin)
{
int n1,n2,c;
muchii_costuri_adiacenta.resize(n+1);
for(int i=0; i<m; ++i)
{
fin>>n1>>n2>>c;
muchii_costuri_adiacenta[n1].push_back(make_pair(n2,c));
}
}
void graf_neorientat::Citire_muchii_costuri_adiacenta(ifstream& fin)
{
int n1,n2,c;
muchii_costuri_adiacenta.resize(n+1);
for(int i=0; i<m; ++i)
{
fin>>n1>>n2>>c;
muchii_costuri_adiacenta[n1].push_back(make_pair(n2,c));
muchii_costuri_adiacenta[n2].push_back(make_pair(n1,c));
}
}
void graf::Citire_muchii_costuri_lista(ifstream& fin)
{
int n1,n2,c;
for(int i=0; i<m; ++i)
{
fin>>n1>>n2>>c;
muchii_costuri_lista.push_back(make_tuple(n1,n2,c));
}
}
bool graf::HavelHakimi(vector<int>& grade)
{
///complexitate: O(n^2 logn)
int suma=0;
int n=grade.size();
for(int i=0; i<n; ++i)
suma+=grade[i];
//daca suma e impara sau unul din grade>n-1 nu e corect
if(suma%2==1)
return 0;
for(int i=0; i<n; ++i)
if(grade[i]>n-1)
return 0;
//sortez descrescator gradele
sort(grade.begin(),grade.end(), greater<int> ());
while(grade[0]!=0)
{
//decrementez cu 1 gradele de la poz 1 la grade[0]
for(int i=1; i<=grade[0]; ++i)
{
grade[i]--;
if(grade[i]<0)
return 0;
}
grade[0]=0;
sort(grade.begin(),grade.end(), greater<int> ());
}
return 1;
}
bool graf_orientat::Exista_lant_nesaturat(int s,int d, vector<int>& tata, vector<vector<int>>& muchii,
vector<vector<int>>& c, vector<vector<int>>& f, vector<int>& viz)
{
queue<int> C;
//golesc viz
viz.clear();
viz.resize(n+1,0);
C.push(s);
viz[s]=1;
tata[d]=0;
//construiesc drum pana la d
while(!C.empty() && tata[d]==0)
{
int v=C.front();
C.pop();
//iau vecinii
for(int i=0; i<muchii[v].size(); ++i)
{
int vecin=muchii[v][i];
if(!viz[vecin] && c[v][vecin]>f[v][vecin])
{
C.push(vecin);
viz[vecin]=1;
tata[vecin]=v;
}
}
}
if(tata[d])
return true; //exista drum pentru ca destinatia are un tata
else
return false;
}
int graf_orientat::Max_flow(int s, int d)
{
///algoritmul Ford-Fulkerson
vector<int> tata;
vector<vector<int>> muchii; //consider graful neorientat
vector<vector<int>> c; //matrice capacitate
vector<vector<int>> f; //matrice flux
vector<int> viz; //pentru a retine nodurile vizitate in bfs
int flux_total=0;
muchii.resize(n+1);
tata.resize(n+1,0);
c.resize(n+1,vector<int>(n+1,0));
f.resize(n+1,vector<int>(n+1,0));
//initializare (muchii, capacitate)
for(int i=1; i<=n; ++i)
for(int j=0; j<muchii_costuri_adiacenta[i].size(); ++j)
{
int nod=get<0>(muchii_costuri_adiacenta[i][j]);
int cost=get<1>(muchii_costuri_adiacenta[i][j]);
muchii[i].push_back(nod);
muchii[nod].push_back(i);
c[i][nod]=cost;
}
while(Exista_lant_nesaturat(s,d,tata,muchii,c,f,viz))
{
//iau toate drumurile care intra in d
for(int i=0; i<muchii[d].size(); ++i)
{
int v=muchii[d][i];
if(f[v][d]!=c[v][d] && viz[v]) //muchie valida
{
tata[d]=v;
int val_minima=110005;//un numar suficient de mare
//aflu cu cat pot modifica fluxul pe drumul de la s la d (pornind din d) => capacitatile reziduale
for (int i=d; i!=s; i=tata[i])
if(c[tata[i]][i]-f[tata[i]][i]<val_minima)
val_minima=c[tata[i]][i]-f[tata[i]][i];
if(val_minima!=0)
//parcurg din nou drumul pentru a actualiza fluxurile
{
for (int i=d; i!=s; i=tata[i])
{
f[tata[i]][i]+=val_minima;
f[i][tata[i]]-=val_minima;
}
flux_total+=val_minima; //actualizez fluxul final
}
}
}
}
return flux_total;
}
int graf::Reprezentant(int x, vector<int>& rad)
{
/// complexitate: O(h arbore), O(1) amortizat
int r=x; //radacina (reprezentantul)
int aux;
while(rad[r]!=r)
r=rad[r];
//compresia drumurilor (unesc nodurile direct de radacina)
while(rad[x]!=x)
{
aux=rad[x];
rad[x]=r;
x=aux;
}
return r;
}
void graf::Reuneste(int x, int y, vector<int>& h, vector<int>& rad)
{
///complexitate: O(1) (dupa aflarea anterioara a reprez. lui x si y)
int rx=Reprezentant(x, rad);
int ry=Reprezentant(y, rad);
if(h[rx]>h[ry]) //unesc arborele mai mic la cel mai mare
{
rad[ry]=rx;
}
else
{
rad[rx]=ry;
if(h[rx]==h[ry])
h[ry]++;
}
}
bool graf::Verifica(int x, int y, vector<int>& rad)
{
///complexitate: O(1) (dupa aflarea anterioara a reprez. lui x si y)
int rx=Reprezentant(x, rad);
int ry=Reprezentant(y, rad);
return (rx==ry);
}
vector<tuple<int,int,int>> graf_neorientat::Apm()
{
///Algoritmul lui Kruskal
///complexitate: O(Mlog*N + Mlog2M)
//lucrez cu muchii_costuri_lista
int nr_sel=0; //numar muchii selectate
int cost_total=0;
vector<int> h; //h[i]=inaltimea maxima (de la compresie) a arborelui care il cuprinde pe i
vector<int> rad;//tata[i]=radacina arborelui in care se afla i
vector<tuple<int,int,int>> muchii_apm;
//initializare
h.resize(n+1);
rad.resize(n+1);
for(int i=1; i<=n; ++i)
rad[i]=i, h[i]=1;
//sortez muchiile crescator dupa cost
sort(muchii_costuri_lista.begin(), muchii_costuri_lista.end(),
[](const tuple<int, int, int> &c1, const tuple<int, int, int> &c2)
{
return get<2>(c1) < get<2>(c2);
});
for(int i=0; i<m && nr_sel<n-1; ++i)
{
int nod1=get<0>(muchii_costuri_lista[i]);
int nod2=get<1>(muchii_costuri_lista[i]);
int cost=get<2>(muchii_costuri_lista[i]);
if(!Verifica(nod1,nod2,rad)) //nu fac parte din aceeasi componenta conexa
{
nr_sel++;
Reuneste(nod1, nod2, h, rad);
cost_total+=cost;
muchii_apm.push_back(make_tuple(nod1,nod2,cost));
}
}
return muchii_apm;
}
ifstream fin("ctc.in");
ofstream fout("ctc.out");
pair<vector<vector<int>>, int> rez;
int main()
{
int n,m;
fin>>n>>m;
graf_orientat g(n,m);
g.Citire_muchii_adiacenta(fin);
rez = g.CTC();
fout << rez.second ;
for(int i=0; i<rez.first.size(); ++i)
{
for(int j=0; j<rez.first[i].size(); ++j)
fout << rez.first[i][j] << " ";
fout << "\n";
}
return 0;
}