/**
解题思路:
本题中的冲突边是指形成环的边且边数大于点数,即本环中所有的边都是冲突边。
不必需边是无向边形成的环,即所要求的桥。
*/
题目大意:要在景点修铁路,给定景点个数n,要修的铁路条数m。如果是不是无向边构成的环形路线,则是不必要的铁路。如果两个环有公共边,则环中的所有边都是冲突边。,问不必要的铁路条数,和冲突边铁路有多少条。
/**
一个没有(关节点)割顶的连通图称为重连通图。
点-双连通分量性质:
不同双连通分量最多只有一个公共点,且它一定是割顶。
任意割顶都是至少两个不同双连通分量的公共点。
边-双连通分量性质:
除了桥不属于任何边-双连通分量之外,其他每条边恰好
属于一个边-双连通分量。
把所有桥删除之后,每个连通分量对应原图中的一个边-双
连通分量。
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<stack>
#include<algorithm>
using namespace std;
const int MAXV=10010;
int pre[MAXV],iscut_v[MAXV],bccno[MAXV];
int dfs_clock,bcc_cnt,bri_num,no_need_num;
vector<int>G[MAXV],bcc[MAXV];
struct Edge{
int u,v;
};
int n,m;
stack <Edge>S;
int dfs(int u,int fa){
int lowu=pre[u]=++dfs_clock; //时钟
int child=0;
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
Edge e=(Edge){u,v};
if(!pre[v]){ //未访问过的点
S.push(e); //存放所有边的栈
child++;
int lowv=dfs(v,u); //计算v的low
lowu=min(lowu,lowv); //用后代的low更新自己
if(lowv>pre[u]){ //桥的判断
bri_num++;
}
if(lowv>=pre[u]){ //割顶的判断,u为割顶
iscut_v[u]=true;
bcc_cnt++; //计算双连通分量的数量
bcc[bcc_cnt].clear(); //清空要加入的那行vector的值
int tmp_edg=0; //计算连通分量中的边数
for(;;){
Edge x= S.top();
S.pop(); //退栈
tmp_edg++;
if(bccno[x.u]!=bcc_cnt){
//如果该点在这个连通分量中
bcc[bcc_cnt].push_back(x.u); //将该点加入该连通分量的vector
bccno[x.u]=bcc_cnt; //标记在哪个连通分量中
}
if(bccno[x.v]!=bcc_cnt){ //同理记录该边的另一端
bcc[bcc_cnt].push_back(x.v);
bccno[x.v]=bcc_cnt;
} //如果将这个分量中的边都退栈了,跳出,不影响u在其他分量中存在
if(x.u==u&&x.v==v){break; }
}
int tmp_ver=0; //记录分量中的顶点个数
for(int i=0;i<n;i++){
if(bccno[i]==bcc_cnt){
tmp_ver++; //同属于一个分量,自加
}
}
if(tmp_ver<tmp_edg){ //如果分量中边大于顶点个数,各个边都会互相影响
no_need_num+=tmp_edg;
}
}
} //pre[v]<pre[u]就是限制仍然有一条边连向已经访问过v再次入栈
else if(pre[v]<pre[u]&&v!=fa){ //如果是反向边
S.push(e);
lowu=min(lowu,pre[v]); //用反向边更新自己
}
}
if(fa<0&&child==1) //如果是根结点,且儿子只有一个
iscut_v[u]=0; //则不是割顶
return lowu;
}
void find_bcc(int n){
memset(pre,0,sizeof(pre));
memset(iscut_v,0,sizeof(iscut_v));
memset(bccno,0,sizeof(bccno));
dfs_clock=bcc_cnt=bri_num=no_need_num=0;
for(int i=0;i<n;i++){ //图可能不是连通图
if(!pre[i])
dfs(i,-1);
}
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF&&(n+m)){
for(int i=0;i<MAXV;i++){
G[i].clear();
}
for(int i=0;i<MAXV;i++){
bcc[i].clear();
}
for(int i=0;i<m;i++){
int a,b;
scanf("%d%d",&a,&b);
G[a].push_back(b);
G[b].push_back(a);
}
find_bcc(n);
printf("%d %d\n",bri_num,no_need_num);
}
}