#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) {};
vector<int> bfs(int start){
vector<int> res(this->adiacenta.size(), -1);
vector<int> vis(this->adiacenta.size());
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(this->noduri);
int res = 0;
stack<int> s;
for(int i=0; i<this->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<unordered_set<int>>& 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;
while(s.top() != stop){
aux.insert(s.top().first);
s.pop();
}
aux.insert(stop.first);
aux.insert(stop.second);
rasp.push_back(aux);
s.pop();
}
}
else{
rev[tata] = min(rev[tata], timp[fiu]);
if(timp[fiu] < timp[tata]){
s.push(make_pair(tata, fiu));
}
}
}
}
vector<unordered_set<int>> biconex(){
vector<int> timp(noduri+1, -1), rev(noduri+1);
stack<pair<int, int>> s;
vector<unordered_set<int>> rasp;
int timpo=0;
//pt ca graful nu e neaparat conex trb sa facem de mai multe ori dfs
for(int i=1; i<=noduri; i++){
if(timp[i]==-1){
dfsBiconex(i, timp, rev, s, timpo, rasp);
}
if(!s.empty()){
unordered_set<int> aux;
while(!s.empty()){
aux.insert(s.top().first);
aux.insert(s.top().second);
s.pop();
}
rasp.push_back(aux);
}
}
return rasp;
}
void dfsCtc(int tata, vector<int>& timp, vector<int>& rev, stack<int>& s, int& timpcurent, vector<unordered_set<int>>& 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]){
while (s.top() != tata){
cout << s.top() << " ";
check.erase(s.top());
s.pop();
}
cout << s.top() << "\n";
check.erase(s.top());
s.pop();
}
}
vector<unordered_set<int>> ctc(){
vector<int> timp(noduri+1, -1), rev(noduri+1);
unordered_set<int> check;
stack<int> s;
vector<unordered_set<int>> 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 bfsDriver(){
ifstream in("bfs.in");
ofstream out("bfs.out");
int n, m, s;
in>>n>>m>>s;
vector<vector<int>> mat(n);
for(int i=0; i<m; i++){
int aux1, aux2;
in>>aux1>>aux2;
mat[aux1-1].push_back(aux2-1);
}
graf x(mat, n, m);
for(int a:x.bfs(s-1)){
out<<a<<" ";
}
}
void dfsDriver(){
ifstream in("dfs.in");
ofstream out("dfs.out");
int n, m;
in>>n>>m;
vector<vector<int>> mat(n);
for(int i=0; i<m; i++){
int aux1, aux2;
in>>aux1>>aux2;
mat[aux1-1].push_back(aux2-1);
mat[aux2-1].push_back(aux1-1);
}
graf x(mat, n, m);
out<<x.dfs();
}
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<unordered_set<int>> fin = x.biconex();
out<<fin.size()<<'\n';
for(auto i:fin){
for(int j:i){
out<<j<<" ";
}
out<<'\n';
}
}
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);
mat[aux2].push_back(aux1);
}
graf x(mat, n, m);
vector<unordered_set<int>> fin = x.ctc();
out<<fin.size()<<'\n';
for(auto i:fin){
for(int j:i){
out<<j<<" ";
}
out<<'\n';
}
}
int main(){
biconexDriver();
return 0;
}