#include <bits/stdc++.h>
#define NIL -1
#define INFINIT INT_MAX
using namespace std;
ifstream fin("apm.in");
ofstream gout("apm.out");
typedef pair<int, int> pereche;
class Graful_meu
{
int _noduri;
int _muchii;
vector<vector<pereche>> lista_adiacenta;
bool _directed, _weighted;
private:
void DFS(int start, bool vizitat[]);
void DFS_Componente_Biconexe(int nod_curent, stack<int>&stiva_noduri, vector<int>&low,
vector<int>&discovery_time,vector<vector<int>>&all_biconex_components);
void DFS_Componente_Tare_Conexe(int nod_curent, stack<int>&stiva_noduri, vector<bool>&is_part_of_a_connected_comp, vector<int>&low,
vector<int>&discovery_time,vector<vector<int>>&all_strongly_connected_components);
void DFS_Critical_Connections(int nod_curent, int parinte, vector<int>&low, vector<int>&discovery_time,
vector<vector<int>>&all_output_components);
public:
Graful_meu(int noduri = 0, int muchii = 0, bool directed = false, bool weighted = false);
void citire_graf();
int getter_numar_noduri();
int Componente_conexe();
void BFS(int start);
vector <vector<int>> Componente_Biconexe();
vector<vector<int>> Componente_Tare_Conexe();
vector<vector<int>> criticalConnections();
vector<int> TopologicalSort();
bool Havel_Hakimi(int n, vector<int>&grade);
// tema - part 2
vector<int> Prim_Algorithm(int &total_weight);
};
Graful_meu::Graful_meu(int noduri, int muchii, bool directed, bool weighted)
{
_noduri = noduri;
_muchii = muchii;
_directed = directed;
_weighted = weighted;
lista_adiacenta.resize(_noduri + 2);
}
void Graful_meu::citire_graf()
{
int x, y;
int cost = 0;
for(int i = 1 ; i <= _muchii ; ++i)
{
fin >> x >> y;
if(_weighted)
fin >> cost;
lista_adiacenta[x].push_back({y, cost});
if(!_directed)
lista_adiacenta[y].push_back({x, cost});
}
}
int Graful_meu::getter_numar_noduri()
{
return _noduri;
}
void Graful_meu::DFS(int start, bool vizitat[])
{
vizitat[start] = 1;
for(auto nod_vecin : lista_adiacenta[start])
if(vizitat[nod_vecin.first] == 0)
DFS(nod_vecin.first, vizitat);
}
int Graful_meu::Componente_conexe()
{
bool vizitat[_noduri + 2] = {0};
int numar_componente_conexe = 0;
for(int i = 1 ; i <= _noduri ; ++i)
if (vizitat[i] == 0)
{
DFS(i, vizitat);
++numar_componente_conexe;
}
return numar_componente_conexe;
}
void Graful_meu::BFS(int start)
{
bool vizitat[_noduri + 2] = {0};
queue<int>coada_BFS;
vector<int> drumuri_minime(_noduri + 2, -1);
coada_BFS.push(start);
vizitat[start] = 1;
drumuri_minime[start] = 0;
while(!coada_BFS.empty())
{
for(auto nod_vecin : lista_adiacenta[start])
if(vizitat[nod_vecin.first] == 0)
{
coada_BFS.push(nod_vecin.first);
vizitat[nod_vecin.first] = 1;
drumuri_minime[nod_vecin.first] = drumuri_minime[start] + 1;
}
coada_BFS.pop();
start = coada_BFS.front();
}
for(int i = 1 ; i <= _noduri ; ++i)
gout << drumuri_minime[i] << " ";
}
vector<vector<int>> Graful_meu::Componente_Biconexe()
{
vector<int>discovery_time(_noduri + 2, NIL);
vector<int>low(_noduri + 2, NIL);
vector<vector<int>> all_biconex_components;
stack<int>stiva_noduri;
for(int i = 1 ; i <= _noduri ; ++i)
if(discovery_time[i] == NIL)
DFS_Componente_Biconexe(i, stiva_noduri, low, discovery_time, all_biconex_components);
return all_biconex_components;
}
void Graful_meu::DFS_Componente_Biconexe(int nod_curent, stack<int>&stiva_noduri, vector<int>&low,
vector<int>&discovery_time,vector<vector<int>>&all_biconex_components)
{
static int time = 0;
low[nod_curent] = discovery_time[nod_curent] = ++time;
stiva_noduri.push(nod_curent);
for(auto vecin : lista_adiacenta[nod_curent])
{
if(discovery_time[vecin.first] == NIL)
{
DFS_Componente_Biconexe(vecin.first, stiva_noduri, low, discovery_time, all_biconex_components);
low[nod_curent] = min(low[nod_curent], low[vecin.first]);
if(low[vecin.first] >= discovery_time[nod_curent])
{
vector<int>one_biconex_component;
int nod_stiva = stiva_noduri.top();
stiva_noduri.pop();
one_biconex_component.push_back(nod_stiva);
while(nod_stiva != vecin.first)
{
nod_stiva = stiva_noduri.top();
stiva_noduri.pop();
one_biconex_component.push_back(nod_stiva);
}
one_biconex_component.push_back(nod_curent);
all_biconex_components.push_back(one_biconex_component);
}
}
else
low[nod_curent] = min(low[nod_curent], discovery_time[vecin.first]);
}
}
vector<vector<int>> Graful_meu::Componente_Tare_Conexe()
{
vector<int>discovery_time(_noduri + 2, NIL);
vector<int>low(_noduri + 2, NIL);
vector<vector<int>> all_strongly_connected_components;
stack<int>stiva_noduri;
vector<bool>is_part_of_a_connected_comp(_noduri + 2, false);
for(int i = 1 ; i <= _noduri ; ++i)
if(discovery_time[i] == NIL)
DFS_Componente_Tare_Conexe(i, stiva_noduri, is_part_of_a_connected_comp, low, discovery_time, all_strongly_connected_components);
return all_strongly_connected_components;
}
void Graful_meu::DFS_Componente_Tare_Conexe(int nod_curent, stack<int>&stiva_noduri, vector<bool>&is_part_of_a_connected_comp, vector<int>&low,
vector<int>&discovery_time,vector<vector<int>>&all_stongly_connected_components)
{
static int time = 0;
low[nod_curent] = discovery_time[nod_curent] = ++time;
stiva_noduri.push(nod_curent);
for(auto vecin : lista_adiacenta[nod_curent])
{
if(discovery_time[vecin.first] == NIL)
{
DFS_Componente_Tare_Conexe(vecin.first, stiva_noduri, is_part_of_a_connected_comp, low, discovery_time, all_stongly_connected_components);
low[nod_curent] = min(low[nod_curent], low[vecin.first]);
}
else
if(is_part_of_a_connected_comp[vecin.first] == false)
low[nod_curent] = min(low[nod_curent], discovery_time[vecin.first]);
}
if(low[nod_curent] == discovery_time[nod_curent])
{
vector<int>one_strongly_connected_component;
int nod_stiva = stiva_noduri.top();
while(nod_stiva != nod_curent)
{
one_strongly_connected_component.push_back(nod_stiva);
is_part_of_a_connected_comp[nod_stiva] = true;
stiva_noduri.pop();
nod_stiva = stiva_noduri.top();
}
one_strongly_connected_component.push_back(nod_stiva);
is_part_of_a_connected_comp[nod_stiva] = true;
stiva_noduri.pop();
all_stongly_connected_components.push_back(one_strongly_connected_component);
}
}
vector<vector<int>> Graful_meu::criticalConnections()
{
vector<vector<int>> lista_adiacenta(_noduri + 2);
vector<int>discovery_time(_noduri + 2, NIL);
vector<int>low(_noduri + 2, NIL);
vector<vector<int>>all_output_components;
for(int i = 1 ; i <= _noduri ; ++i)
if(discovery_time[i] == -1)
DFS_Critical_Connections(i, -1, low, discovery_time, all_output_components);
return all_output_components;
}
void Graful_meu::DFS_Critical_Connections(int nod_curent, int parinte, vector<int>&low, vector<int>&discovery_time,
vector<vector<int>>&all_output_components)
{
static int time = 0;
low[nod_curent] = discovery_time[nod_curent] = ++time;
for(auto vecin : lista_adiacenta[nod_curent])
{
if(discovery_time[vecin.first] == NIL)
{
parinte = nod_curent;
DFS_Critical_Connections(vecin.first, parinte, low,discovery_time, all_output_components);
low[nod_curent] = min(low[nod_curent], low[vecin.first]);
if(low[vecin.first] > discovery_time[nod_curent])
all_output_components.push_back({nod_curent, vecin.first});
}
else
if(parinte != vecin.first)
low[nod_curent] = min(low[nod_curent], discovery_time[vecin.first]);
}
}
vector<int> Graful_meu::TopologicalSort()
{
int grad_interior[_noduri + 2] = {0};
vector<int>ordine_topologica;
for(int i = 1 ; i <= _noduri ; ++i)
for(auto vecin : lista_adiacenta[i])
++grad_interior[vecin.first];
int copie_numar_noduri = _noduri;
while(copie_numar_noduri)
{
for(int nod_curent = 1 ; nod_curent <= _noduri ; ++nod_curent)
{
if(grad_interior[nod_curent] == 0)
{
ordine_topologica.push_back(nod_curent);
for(auto vecin : lista_adiacenta[nod_curent])
--grad_interior[vecin.first];
grad_interior[nod_curent] = NIL;
--copie_numar_noduri;
}
}
}
return ordine_topologica;
}
bool Graful_meu::Havel_Hakimi(int n, vector<int>&grade)
{
int suma = 0;
for(auto element : grade)
{
suma += element;
if(element > n - 1)
return false;
}
if(suma % 2)
return false;
while(all_of(grade.begin(), grade.end(), [](int i) {return i == 0;}) == false)
{
sort(grade.begin(), grade.end(), greater<int>());
int valoare_grad = grade[0];
grade.erase(grade.begin());
for(int i = 0 ; i < valoare_grad ; ++i)
{
--grade[i];
if(grade[i] < 0)
return false;
}
}
return true;
}
// algoritmul lui Prim implementat cu priority queue si va returna vectorul de tati
vector<int> Graful_meu::Prim_Algorithm(int &total_weight)
{
priority_queue<pereche, vector<pereche>, greater<pereche>> min_heap;
vector<int> cost(_noduri + 2, INFINIT);
vector<int> tata(_noduri + 2, NIL);
vector<bool> inAPM(_noduri + 2, false);
total_weight = 0;
min_heap.push({0, 1});
cost[1] = 0;
while(!min_heap.empty())
{
int nod_curent = min_heap.top().second;
int cost_curent = min_heap.top().first;
min_heap.pop();
if(inAPM[nod_curent] == true)
continue;
total_weight += cost_curent;
inAPM[nod_curent] = true;
for(auto vecin : lista_adiacenta[nod_curent])
{
int cost_vecin = vecin.second;
int nod_vecin = vecin.first;
if(inAPM[nod_vecin] == false and cost[nod_vecin] > cost_vecin)
{
cost[nod_vecin] = cost_vecin;
min_heap.push({cost_vecin, nod_vecin});
tata[nod_vecin] = nod_curent;
}
}
}
return tata;
}
int main()
{
//citire numar de noduri si numar de muchii(inafara de Havel Hakimi)
int noduri, muchii;
fin >> noduri >> muchii;
// DFS - Componente conexe
/*
Graful_meu g(noduri, muchii, false);
g.citire_graf();
gout << g.Componente_conexe();
*/
// BFS
/*
int nod_start;
fin >> nod_start;
Graful_meu g(noduri, muchii, true);
g.citire_graf();
g.BFS(nod_start);
*/
// Componente biconexe - Algoritm Tarjan modificat
/*
Graful_meu g(noduri, muchii, false);
g.citire_graf();
vector<vector<int>>toate_componentele_biconexe = g.Componente_Biconexe();
gout << toate_componentele_biconexe.size() << '\n';
for(auto &one_biconex_comp : toate_componentele_biconexe)
{
for(auto element : one_biconex_comp)
gout << element << " ";
gout << '\n';
}
*/
// Componente tare conexe - Algoritm Tarjan
/*
Graful_meu g(noduri, muchii, true);
g.citire_graf();
vector<vector<int>>toate_componentele_tare_conexe = g.Componente_Tare_Conexe();
gout << toate_componentele_tare_conexe.size() << '\n';
for(auto &one_strongly_connected_comp : toate_componentele_tare_conexe)
{
for(auto element : one_strongly_connected_comp)
gout << element << " ";
gout << '\n';
}
*/
// Critical connections - Algoritm Tarjan - same
/*
Graful_meu g(noduri, muchii, false);
g.citire_graf();
vector<vector<int>>crit_conn = g.criticalConnections();
gout << crit_conn.size() << '\n';
for(auto &one_crit_conn : crit_conn)
{
for(auto element : one_crit_conn)
gout << element << " ";
gout << '\n';
}
*/
// Sortare topologica
/*
Graful_meu g(noduri, muchii, true);
g.citire_graf();
vector<int>ordine_topologica = g.TopologicalSort();
for(auto nod : ordine_topologica)
gout << nod << " ";
*/
// Havel Hakimi
/*
int n;
fin >> n;
Graful_meu g(n);
vector<int>grade(n + 2);
for(int i = 0; i < n; ++i)
fin >> grade[i];
bool formare_graf = g.Havel_Hakimi(n, grade);
if(formare_graf)
gout << "Se poate forma graful!";
else
gout << "Nu se poate forma graful!";
*/
// APM - Prim's Algorithm
int cost_total;
Graful_meu g(noduri, muchii, false, true);
g.citire_graf();
vector<int>tata = g.Prim_Algorithm(cost_total);
gout << cost_total << '\n' << g.getter_numar_noduri() - 1 << '\n';
for(int i = 2 ; i <= g.getter_numar_noduri(); ++i)
gout << i << " " << tata[i] << '\n';
return 0;
}