#include <bits/stdc++.h>
using namespace std;
///Clasa pentru reprezentare de grafuri
class Graf
{
private:
int nrNoduri;
int nrMuchii;
vector<vector<int>> lisAdiacenta;
vector<int> BFS(const int nodStart);
void DFS(const int nodStart, vector<bool> &vizitat, stack<int> &stiva);
void ctcDFS(int x, vector<int> &disc, vector<int> &low, stack<int> &stiva, vector<bool> &gasitStiva, vector<vector<int>> &rez);
void critConDFS(int x, vector<bool> &viz, vector<int> &disc, vector<int> &low, vector<int> &tata, ostream &out);
void biconexeDFS(int x, vector<int> &disc, vector<int> &low, stack<pair<int, int>> &comp, vector<int> &tata, vector<set<int>> &rez);
public:
Graf(const int nrNoduriDat, const int nrMuchiiDat);
Graf(const int nrNoduriDat, const int nrMuchiiDat, const vector<vector<int>> &listaData);
Graf(const Graf &grafDat);
~Graf();
int getNrNoduri();
int getNrMuchii();
Graf& operator= (const Graf &grafDat);
friend std::ostream& operator<<(std::ostream &out, const Graf &grafDat);
void citireOrientat(istream &in);
void citireNeorientat(istream &in);
void afisareDistanteBFS(const int nodStart, ostream &out);
int nrCompConexe();
void sortareTopologica(ostream &out);
bool Havel_Hakimi(vector<int> &grade);
static void existaGraf(vector<int> &grade, ostream &out);
void CTC(ostream &out);
void CriticalConnections(ostream &out);
void Biconex(ostream &out);
};
///CONSTRUCTORI
Graf :: Graf(const int nrNoduriDat, const int nrMuchiiDat) //constructor parametrizat - nu avem lista de adiacenta
{
nrNoduri = nrNoduriDat;
nrMuchii = nrMuchiiDat;
vector<int> aux(1,-1); //initializam prima pozitie cu -1 pentru ca indexarea e de la 1
for(int i = 0; i <= nrNoduri+1; ++i) {
lisAdiacenta.push_back(aux);
}
}
Graf :: Graf(const int nrNoduriDat, const int nrMuchiiDat, const vector<vector<int>> &listaData) //constructor parametrii - avem lista adiacenta
{
nrNoduri = nrNoduriDat;
nrMuchii = nrMuchiiDat;
lisAdiacenta = listaData;
}
Graf :: Graf(const Graf & grafDat) //constructor copiere
{
nrNoduri = grafDat.nrNoduri;
nrMuchii = grafDat.nrMuchii;
lisAdiacenta = grafDat.lisAdiacenta;
}
///DESTRUCTOR
Graf :: ~Graf()
{
lisAdiacenta.clear();
}
///GETTERI
int Graf :: getNrNoduri() //getter numar noduri
{
return nrNoduri;
}
int Graf :: getNrMuchii() //getter numar muchii
{
return nrMuchii;
}
///SUPRAINCARCARI OPERATORI
Graf& Graf :: operator= (const Graf &grafDat) //supraincarcare operator egal
{
if(this != &grafDat) {
this->nrNoduri = grafDat.nrNoduri;
this->nrMuchii = grafDat.nrMuchii;
if(!this->lisAdiacenta.empty())
lisAdiacenta.clear();
this->lisAdiacenta = grafDat.lisAdiacenta;
}
return *this;
}
ostream& operator<< (std::ostream& out, const Graf& grafDat) //supraincarcare operator afisare
{
out << "Numarul de noduri ale grafului este: " << grafDat.nrNoduri << "\n";
out << "Numarul de muchii ale grafului este: " << grafDat.nrMuchii << "\n";
for (int i = 1; i <= grafDat.nrNoduri; ++i) {
out << i << ": ";
for (int j = 1; j < grafDat.lisAdiacenta[i].size(); ++j) {
out << grafDat.lisAdiacenta[i][j]<<" ";
}
out<<"\n";
}
return out;
}
///METODE CITIRI
void Graf :: citireNeorientat(istream &in) //citim de la tastatura sau din fisier un graf neorientat
{
int x, y;
for(int i = 1; i <= nrMuchii; ++i) {
in >> x >> y;
lisAdiacenta[x].push_back(y), lisAdiacenta[y].push_back(x);
}
}
void Graf :: citireOrientat(istream &in) //citim de la tastatura sau din fisier un graf orientat
{
int x, y;
for(int i = 1; i <= nrMuchii; ++i) {
in >> x >> y;
lisAdiacenta[x].push_back(y);
}
}
///METODE IMPLEMENTARE ALGORITMI
vector<int> Graf :: BFS(const int nodStart) //parcurgere in latime
{
queue<int> Q;
int x, y;
vector<int> distanta(nrNoduri+1, -1);
distanta[nodStart] = 0;
Q.push(nodStart);
while(!Q.empty()) {
x = Q.front(); //eliminam nodul curent din coada
Q.pop();
for (int i = 1; i < lisAdiacenta[x].size(); ++i)
if (distanta[lisAdiacenta[x][i]] == -1) { //daca nu a fost vizitat vecinul
distanta[lisAdiacenta[x][i]] = distanta[x] + 1; //inseamna ca am gasit drum mai scurt
Q.push(lisAdiacenta[x][i]);
}
}
return distanta;
}
void Graf :: afisareDistanteBFS(const int nodStart, ostream &out) //afisam vectorul de distante de la BFS
{
vector <int> distanta = BFS(nodStart);
for(int i = 1; i <= nrNoduri; ++i) {
out << distanta[i] << " ";
}
}
void Graf :: DFS(const int x, vector<bool> &vizitat, stack<int> &stiva) //parcurgere in adancime
{
vizitat[x] = 1;
for(int i = 1; i < lisAdiacenta[x].size(); i++)
if(vizitat[lisAdiacenta[x][i]] == 0) //daca vecinul sau nu a fost vizitat, mergem la el
DFS(lisAdiacenta[x][i], vizitat, stiva);
stiva.push(x);
}
int Graf :: nrCompConexe() //numar de componente conexe facut cu DFS
{
stack<int> stiva;
int nrCompCon = 0;
vector<bool> vizitat(nrNoduri+1, 0);
for(int i = 1; i <= nrNoduri; ++i )
if(!vizitat[i]) {
nrCompCon++;
DFS(i, vizitat, stiva);
}
return nrCompCon;
}
void Graf :: sortareTopologica(ostream &out) //afisam o sortare topologica posibila
{
stack<int> stiva;
vector<bool> vizitat(nrNoduri+1, 0);
for(int i = 1; i <= nrNoduri; ++i )
if(!vizitat[i])
DFS(i, vizitat, stiva);
while (!stiva.empty()) {
out << stiva.top() << " ";
stiva.pop();
}
}
void Graf :: existaGraf(vector<int> &grade, ostream &out)
{
vector<int> copieGrade;
copieGrade = grade;
int S = 0; //suma grade
for (int i = 0; i < grade.size(); ++i)
S += grade[i];
if (S % 2 == 1) { //suma gradelor intr-un graf neorientat e mereu para
out << "Nu\n";
return;
}
sort(grade.begin(), grade.end(), greater<int>()); //luam gradele in ordine descrescatoare
while (true) {
if (grade[0] == 0) {
out << "Da";
return;
}
int fst = grade[0];
grade.erase(grade.begin() + 0);
if (fst > grade.size()) { //daca avem un grad mai mare decat numarul de noduri ramase
out << "Nu";
return;
}
for (int i = 0; i < fst; ++i) {
grade[i]--;
if (grade[i] < 0) {
out << "Nu";
return;
}
}
int j = fst;
for (int i = 0; i < fst; ++i) {
if(j >= grade.size())
break;
if (grade[i] < grade[j]) {
swap(grade[i], grade[j]);
j++;
}
}
}
}
void Graf::ctcDFS(int x, vector<int> &disc, vector<int> &low, stack<int> &stiva, vector<bool> &gasitStiva, vector<vector<int>> &rez)
{
static int timp = 0;
disc[x] = low[x] = ++timp;
stiva.push(x);
gasitStiva[x] = 1;
for (int i = 1; i < lisAdiacenta[x].size(); i++) {
if (disc[lisAdiacenta[x][i]] == -1) {
ctcDFS(lisAdiacenta[x][i], disc, low, stiva, gasitStiva, rez);
low[x] = min(low[x], low[lisAdiacenta[x][i]]);
}
else if (gasitStiva[lisAdiacenta[x][i]] == 1)
low[x] = min(low[x], disc[lisAdiacenta[x][i]]);
}
vector<int> compNoua;
if (low[x] == disc[x]) {
while (stiva.top() != x) {
compNoua.push_back(stiva.top());
gasitStiva[stiva.top()] = 0;
stiva.pop();
}
compNoua.push_back(stiva.top());
gasitStiva[stiva.top()] = 0;
rez.push_back(compNoua);
stiva.pop();
}
}
void Graf :: CTC(ostream &out){
vector<int> disc(nrNoduri+1, -1);
vector<int> low(nrNoduri+1, -1);
stack<int> stiva;
vector<bool> gasitStiva(nrNoduri+1, 0);
vector<vector<int>> rez;
for (int i = 1; i <= nrNoduri; ++i) {
if (disc[i] == -1)
ctcDFS(i, disc, low, stiva, gasitStiva, rez);
}
out << rez.size() << "\n";
for (int i = 0; i < rez.size(); ++i) {
for (int j = 0; j < rez[i].size(); ++j) {
out << rez[i][j] << " ";
}
out << "\n";
}
}
void Graf::critConDFS(int x, vector<bool> &viz, vector<int> &disc, vector<int> &low, vector<int> &tata, ostream &out)
{
static int timp = 0;
viz[x] = 1;
disc[x] = low[x] = ++timp;
for (int i = 1; i < lisAdiacenta[x].size(); ++i) {
if (!viz[lisAdiacenta[x][i]]) {
tata[lisAdiacenta[x][i]] = x;
critConDFS(lisAdiacenta[x][i], viz, disc, low, tata, out);
low[x] = min(low[x], low[lisAdiacenta[x][i]]);
if (low[lisAdiacenta[x][i]] > disc[x]) {
out << x << " " << lisAdiacenta[x][i] << "\n";
}
}
else if (lisAdiacenta[x][i] != tata[x])
low[x] = min(low[x], disc[lisAdiacenta[x][i]]);
}
}
void Graf :: CriticalConnections(ostream &out) {
vector<bool> viz(nrNoduri+1, 0);
vector<int> disc(nrNoduri+1);
vector<int> low(nrNoduri+1);
vector<int> tata(nrNoduri+1, -1);
for (int i = 1; i <= nrNoduri; ++i) {
if (!viz[i])
critConDFS(i, viz, disc, low, tata, out);
}
}
void Graf :: biconexeDFS(int x, vector<int> &disc, vector<int> &low, stack<pair<int, int>> &comp, vector<int> &tata, vector<set<int>> &rez)
{
static int timp = 0;
disc[x] = low[x] = ++timp;
int nrCopii = 0;
for (int i = 1; i < lisAdiacenta[x].size(); ++i) {
if (disc[lisAdiacenta[x][i]] == -1) {
nrCopii++;
tata[lisAdiacenta[x][i]] = x;
comp.push(make_pair(x, lisAdiacenta[x][i]));
biconexeDFS(lisAdiacenta[x][i], disc, low, comp, tata, rez);
low[x] = min(low[x], low[lisAdiacenta[x][i]]);
if ((disc[x] == 1 && nrCopii > 1) || (disc[x] > 1 && low[lisAdiacenta[x][i]] >= disc[x])) {
set<int> noduriCompNoua;
while (comp.top().first != x || comp.top().second != lisAdiacenta[x][i]) {
noduriCompNoua.insert(comp.top().first);
noduriCompNoua.insert(comp.top().second);
comp.pop();
}
noduriCompNoua.insert(comp.top().first);
noduriCompNoua.insert(comp.top().second);
rez.push_back(noduriCompNoua);
comp.pop();
}
}
else if (lisAdiacenta[x][i] != tata[x]) {
low[x] = min(low[x], disc[lisAdiacenta[x][i]]);
if (disc[lisAdiacenta[x][i]] < disc[x]) {
comp.push(make_pair(x, lisAdiacenta[x][i]));
}
}
}
}
void Graf :: Biconex(ostream &out) {
vector<int> tata(nrNoduri+1, -1);
vector<int> disc(nrNoduri+1, -1);
vector<int> low(nrNoduri+1, -1);
stack<pair<int, int>> comp;
vector<set<int>> rez;
for (int i = 1; i <= nrNoduri; ++i) {
if (disc[i] == -1)
biconexeDFS(i, disc, low, comp, tata, rez);
bool compNoua = 0;
set<int> noduriCompNoua;
while (!comp.empty()) {
compNoua = 1;
noduriCompNoua.insert(comp.top().first);
noduriCompNoua.insert(comp.top().second);
comp.pop();
}
if (compNoua) {
rez.push_back(noduriCompNoua);
}
}
out << rez.size();
}
///Antete functii pentru rezolvarea problemelor
void Rezolva_DFSComponenteConexe(); //https://infoarena.ro/problema/dfs - 100pc
void Rezolva_BFS(); //https://www.infoarena.ro/problema/bfs - 100pc
void Rezolva_SortareTopologica(); //https://infoarena.ro/problema/sortaret - 100pc
void Rezolva_Havel_Hakimi(); //exemple: (DA:(5 5 5 5 5 5),(5 5 5 5 4 4),(3 3 3 3),(4 4 4 4 4),(2 2 1 1),(2 2 1 1 0),(2 0 1 2 1),(2 1 2 1 2 1 2 3)
//(NU:(5 5 5 5 5 4),(3 2 1 0), (3 3 3 3 3)
void Rezolva_CTC();
void Rezolva_Critical_Connection();
void Rezolva_Biconex();
///Driver code
int main()
{
Rezolva_Biconex();
return 0;
}
///Corpurile functiilor
void Rezolva_DFSComponenteConexe()
{
ifstream fin("dfs.in");
ofstream fout("dfs.out");
int N, M;
fin >> N >> M;
Graf graf(N, M);
graf.citireNeorientat(fin);
fout<<graf.nrCompConexe();
}
void Rezolva_BFS()
{
ifstream fin("bfs.in");
ofstream fout("bfs.out");
int N, M, S;
fin >> N >> M >> S;
Graf graf(N, M);
graf.citireOrientat(fin);
graf.afisareDistanteBFS(S, fout);
}
void Rezolva_SortareTopologica()
{
ifstream fin("sortaret.in");
ofstream fout("sortaret.out");
int N, M;
fin >> N >> M;
Graf graf(N, M);
graf.citireOrientat(fin);
graf.sortareTopologica(fout);
}
void Rezolva_Havel_Hakimi()
{
ifstream fin("havelhakimi.in");
ofstream fout("havelhakimi.out");
vector<int> grade;
int N, x;
fin >> N;
for (int i = 0; i < N; i++) {
fin >> x;
grade.push_back(x);
}
Graf :: existaGraf(grade, fout);
}
void Rezolva_CTC()
{
ifstream fin("ctc.in");
ofstream fout("ctc.out");
int N, M;
fin >> N >> M;
Graf graf(N, M);
graf.citireOrientat(fin);
graf.CTC(fout);
}
void Rezolva_Critical_Connection()
{
ifstream fin("critcon.in");
ofstream fout("critcon.out");
int N, M;
fin >> N >> M;
Graf graf(N, M);
graf.citireNeorientat(fin);
graf.CriticalConnections(fout);
}
void Rezolva_Biconex()
{
ifstream fin("biconex.in");
ofstream fout("biconex.out");
int N, M;
fin >> N >> M;
Graf graf(N, M);
graf.citireNeorientat(fin);
graf.Biconex(fout);
}