#include <iostream>
#include <fstream>
#include <bits/stdc++.h>
using namespace std;
class graf{
int noduri, muchii;
vector<vector<int>> adiacenta;
public:
graf(vector<vector<int>> listaAdiacenta, int noduri, int muchii): adiacenta(listaAdiacenta), noduri(noduri), muchii(muchii) {};
void citireNeorientat(ifstream& in){
for(int i=0; i<muchii; i++){
int aux1, aux2;
in>>aux1>>aux2;
adiacenta[aux1].push_back(aux2);
adiacenta[aux2].push_back(aux1);
}
}
vector<int> bfs(int start){
vector<int> res(noduri+1, -1);
vector<int> vis(noduri+1);
queue<int> q;
vis[start] = 1;
q.push(start);
res[start] = 0;
while(!q.empty()){
int curr = q.front();
q.pop();
for (int i: adiacenta[curr])
if (vis[i] == 0) {
vis[i] = 1;
q.push(i);
res[i] = res[curr] + 1;
};
}
return res;
}
int dfs(){
vector<int> vis(noduri+1);
int res = 0;
stack<int> s;
for(int i=1; i<=noduri; i++){
if(vis[i] == 0){
res++;
vis[i]=1;
s.push(i);
while(!s.empty()){
int curr = s.top();
s.pop();
for(int i=adiacenta[curr].size()-1; i>=0; i--){
int nod = adiacenta[curr][i];
if(vis[nod] == 0){
vis[nod] = 1;
s.push(nod);
}
}
}
}
}
return res;
}
void dfsbiconex(int tata, vector<int>& timp, vector<int>& rev, stack<pair<int, int>>& s, int& timpcurent, vector<string>& rasp){
timpcurent++;
timp[tata]=timpcurent;
rev[tata]=timpcurent;
for(int fiu:adiacenta[tata]){
if(timp[fiu]==-1){
s.push(make_pair(tata, fiu));
dfsbiconex(fiu, timp, rev, s, timpcurent, rasp);
rev[tata]=min(rev[tata], rev[fiu]);
if((timp[tata]!=1 && rev[fiu]>=timp[tata]) || (timp[tata]==1 && adiacenta[tata].size()>1)){
pair<int, int> stop = make_pair(tata, fiu);
unordered_set<int> aux;
string str="";
aux.insert(stop.first);
aux.insert(stop.second);
str+=to_string(stop.first);
str+=" ";
str+=to_string(stop.second);
str+=" ";
while(s.top() != stop){
int nodcur = s.top().first;
if(aux.find(nodcur)==aux.end()){
str+=to_string(nodcur);
str+=" ";
aux.insert(s.top().first);
}
s.pop();
}
str+='\n';
rasp.push_back(str);
s.pop();
}
}
else{
rev[tata] = min(rev[tata], timp[fiu]);
if(timp[fiu] < timp[tata]){
s.push(make_pair(tata, fiu));
}
}
}
}
vector<string> biconex(){
vector<int> timp(noduri+1, -1), rev(noduri+1);
stack<pair<int, int>> s;
vector<string> rasp;
int timpo=0;
for(int i=0; i<noduri; i++){
if(timp[i]==-1){
dfsbiconex(i, timp, rev, s, timpo, rasp);
}
if(!s.empty()){
unordered_set<int> aux;
string str;
while(!s.empty()){
if(aux.find(s.top().first)==aux.end()){
aux.insert(s.top().first);
str+=to_string(s.top().first);
str+=" ";
}
if(aux.find(s.top().second)==aux.end()){
aux.insert(s.top().second);
str+=to_string(s.top().second);
str+=" ";
}
s.pop();
}
str+='\n';
rasp.push_back(str);
}
}
return rasp;
}
void dfsCtc(int tata, vector<int>& timp, vector<int>& rev, stack<int>& s, int& timpcurent, vector<string>& rasp, unordered_set<int>& check){
timpcurent++;
timp[tata]=timpcurent;
rev[tata]=timpcurent;
s.push(tata);
check.insert(tata);
for(int fiu:adiacenta[tata]){
if(timp[fiu]==-1){
dfsCtc(fiu, timp, rev, s, timpcurent, rasp, check);
rev[tata]=min(rev[tata], rev[fiu]);
}
else if(check.find(fiu)!=check.end()){
rev[tata] = min(rev[tata], timp[fiu]);
}
}
if(timp[tata]==rev[tata]){
string aux="";
while (s.top() != tata){
aux+=to_string(s.top());
aux+=" ";
check.erase(s.top());
s.pop();
}
aux+=to_string(s.top());
aux+=" ";
check.erase(s.top());
s.pop();
aux+='\n';
rasp.push_back(aux);
}
}
vector<string> ctc(){
vector<int> timp(noduri+1, -1), rev(noduri+1);
unordered_set<int> check;
stack<int> s;
vector<string> rasp;
int timpo=0;
for(int i=1; i<=noduri; i++){
if(timp[i]==-1){
dfsCtc(i, timp, rev, s, timpo, rasp, check);
}
}
return rasp;
}
void dfsSortaret(int tata, vector<int>& vis, vector<int>& res){
for(int fiu:adiacenta[tata]){
if(!vis[fiu]){
vis[fiu]=1;
dfsSortaret(fiu, vis, res);
}
}
res.push_back(tata);
}
vector<int> sortaret(){
vector<int> vis(this->noduri+1);
vector<int> res;
for(int i=1; i<=this->noduri; i++){
if(vis[i] == 0){
vis[i]=1;
dfsSortaret(i, vis, res);
}
}
return res;
}
void dfsCriticalConnections(int tata, vector<int>& timp, vector<int>& rev, int& timpcurent, vector<vector<int>>& rasp, vector<int> &parinte){
timpcurent++;
timp[tata]=timpcurent;
rev[tata]=timpcurent;
for(int fiu:adiacenta[tata]){
parinte[fiu]=tata;
if(timp[fiu]==-1){
dfsCriticalConnections(fiu, timp, rev, timpcurent, rasp, parinte);
rev[tata]=min(rev[tata], rev[fiu]);
if(rev[fiu]>timp[tata]){
vector<int> aux;
aux.push_back(tata);
aux.push_back(fiu);
rasp.push_back(aux);
}
}
else if(parinte[tata]!=fiu){
rev[tata] = min(rev[tata], timp[fiu]);
}
}
}
vector<vector<int>> criticalConnections(){
vector<int> timp(noduri+1, -1), rev(noduri+1), parinte(noduri+1);
vector<vector<int>> rasp;
int timpo=0;
for(int i=1; i<=noduri; i++){
if(timp[i]==-1){
dfsCriticalConnections(i, timp, rev, timpo, rasp, parinte);
}
}
return rasp;
}
};
bool havelHakimi(vector<int>& a){
sort(a.begin(), a.end(), [](int m, int n){return m>n;});
int x = a[0];
if(a.size()==1 && x>=1){
return false;
}
if(x==0){
return true;
}
a.erase(a.begin()+0);
if(x>a.size()){
return false;
}
for(int i=0; i<x; i++){
a[i]--;
if(a[i]<0){
return false;
}
}
return havelHakimi(a);
}
void bfsDriver(){
ifstream in("bfs.in");
ofstream out("bfs.out");
int n, m, s;
in>>n>>m>>s;
vector<vector<int>> mat(n+1);
for(int i=0; i<m; i++){
int aux1, aux2;
in>>aux1>>aux2;
mat[aux1].push_back(aux2);
}
graf x(mat, n, m);
vector<int> rasp = x.bfs(s);
for(int i=1; i<=n; i++){
out<<rasp[i]<<" ";
}
}
void dfsDriver(){
ifstream in("dfs.in");
ofstream out("dfs.out");
int n, m;
in>>n>>m;
vector<vector<int>> mat(n+1);
graf x(mat, n, m);
x.citireNeorientat(in);
out<<x.dfs();
in.close();
out.close();
}
void biconexDriver(){
ifstream in("biconex.in");
ofstream out("biconex.out");
int n, m;
in>>n>>m;
vector<vector<int>> mat(n+1);
for(int i=0; i<m; i++){
int aux1, aux2;
in>>aux1>>aux2;
mat[aux1].push_back(aux2);
mat[aux2].push_back(aux1);
}
graf x(mat, n, m);
vector<string> fin = x.biconex();
out<<fin.size()<<'\n';
for(string i:fin){
out<<i;
}
}
void ctcDriver(){
ifstream in("ctc.in");
ofstream out("ctc.out");
int n, m;
in>>n>>m;
vector<vector<int>> mat(n+1);
for(int i=0; i<m; i++){
int aux1, aux2;
in>>aux1>>aux2;
mat[aux1].push_back(aux2);
}
graf x(mat, n, m);
vector<string> fin = x.ctc();
out<<fin.size()<<'\n';
for(string i:fin){
out<<i;
}
}
void sortaretDriver(){
ifstream in("sortaret.in");
ofstream out("sortaret.out");
int n, m;
in>>n>>m;
vector<vector<int>> mat(n+1);
for(int i=0; i<m; i++){
int aux1, aux2;
in>>aux1>>aux2;
mat[aux1].push_back(aux2);
}
graf x(mat, n, m);
vector<int> res=x.sortaret();
for(int i=res.size()-1; i>=0; i--){
out<<res[i]<<" ";
}
}
void havelHakimiDriver(){
ifstream in("havelhakimi.in");
ofstream out("havelhakimi.out");
int n;
in>>n;
vector<int> a;
for(int i=0; i<n; i++){
int aux;
in>>aux;
a.push_back(aux);
}
out<<havelHakimi(a);
}
void criticalConnectionsDriver(){
int n, m;
cin>>n>>m;
vector<vector<int>> mat(n+1);
for(int i=0; i<m; i++){
int aux1, aux2;
cin>>aux1>>aux2;
mat[aux1].push_back(aux2);
mat[aux2].push_back(aux1);
}
graf x(mat, n, m);
vector<vector<int>> fin = x.criticalConnections();
for(auto i:fin){
for(auto j:i){
cout<<j<<" ";
}
}
}
int main(){
dfsDriver();
return 0;
}