#include<iostream>
#include <vector>
#include <string.h>
#include <algorithm>
using namespace std;
const int nMax = 100000+10;
bool cut[nMax];
int Dfn[nMax], low[nMax], parent[nMax];
int vis[nMax];
vector<int> adj[nMax], CutEdge[nMax];
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void findcut(int dep, int u) {
Dfn[u] = low[u] = dep;
for (int i=0; i<adj[u].size(); i++) {
int v = adj[u][i];
// cout << v << ":"<<dfn[v]<< endl;
//if (!dfn[v])//用这个条件判断会出现错误......
if(!vis[v])
{
vis[v] = true;
parent[v] = u;
// cout << v <<"parent" << u << endl;
findcut(dep+1, v);
/*if (u == rt)
rt_num++;*/
low[u] = min(low[u], low[v]);//确保low[u]最小
if (low[v] >= Dfn[u])//1 :: 1 1 3 :: 2 2
{
// cout << parent[v] << endl;
cut[u] = true; //在例子中....1被3拖出来了......明明1是叶子结点...(ps:找出问题啦。。不能以dfn作为判断条件...不过不明白...明明走过了的确就不为0了
CutEdge[u].push_back(v);//割边
/*
7 6
1 3
2 3
3 4
3 5
4 5
5 6
*/
// cout<< v << ":" << u << CutEdge[u].size() << endl; //如果满足这个条件则u 为割点,//u-v即为割边
}
}
else if(parent[u] != v && Dfn[v] < low[u])//v的父节点不能用来跟新他的low值
{
int tmp = low[u];
// cout << v << "parent" << parent[v] << "u:" << u << endl;
low[u] = min(low[u],Dfn[v]);//回溯继续
// if(u == 3 && low[3] == 1) cout <<parent[3] << v << endl;
// cout<< u << "修改前后比较:"<< tmp << " :" << low[u] << " :" << v << endl;
}
}
// cout << "此时的" << ":" << low[u] << " " << u << " u: " << dfn[u] <<endl;
}
int main() {
//初始化
for(int i=0; i<nMax; i++){
adj[i].clear();
CutEdge[i].clear();
}
int u, v, n, m;
while (~scanf("%d%d", &n, &m))
{
memset(vis, false, sizeof(vis));
memset(cut, false, sizeof(cut));
// memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(parent, 0, sizeof(parent));
//e = 0;
// memset(head, -1, sizeof(head));
for(int i=0; i<m; i++){
cin >> u >> v;
addEdge(u, v);
addEdge(v, u);
}
//建好图后调用
for(int i=1; i<=n; i++)
findcut(1, i); //以i为根节点,可以在下次选另一点为根节点时,判断上次的根结点 是否为割点
//另一种判断根节点是否为割点,及判断根结点有几颗子树,代码如下:
/* Dfn[1] = low[1] = 1;
int childTree = 0;
for (int i=0; i<adj[1].size(); i++) {
int w = adj[1][i];
if(!vis[w]){
vis[w] = true;
parent[w] = 0;
findcut(2, w);
if(low[w] > Dfn[1]){
CutEdge[1].push_back(w);
}
childTree++;
}
}
if(childTree >= 2){//根顶点是割点的条件
cut[1] = true;
} */
//输出割点个数
int cnt = 0;
for(int i=1; i<=n; i++){
if(cut[i] == true){
cnt++;
}
}
cout<< cnt << endl;
for(int i=1; i<=n; i++){
for(int j=0; j<CutEdge[i].size(); j++){
cout << i << "---" << CutEdge[i][j] <<endl;
}
cout <<i << "size" << CutEdge[i].size() << endl;
}
}
//则cut 中为 true 的即为割点
return 0;
}