/*
题目描述:给出一张无向图,分别求出图中满足如下两种条件的边的数量:
1、该边不在任何简单环当中;
2、该边在多个简单环当中;
方法:不在任何简单环当中的,就是图中的桥,可以求出来;
通过简单环联想到点双连通分量,找到某个双连通分量,若该双连通分量包含的边数超过该点双连通分量包含的点数,那么这个双
连通分量中的每条边都是满足条件2的边;
综上,用dfs求出桥的数量与上述双连通分量中的边数输出即可;
可以套用求双连通分量的模板来求上述二者
求桥:在满足割点条件时额外判断是不是桥,若是,桥的数量+1;
求BCC中的边数:在dfs的过程中,需要从栈中取出该分量中的每一条边,每取出一条,边数+1,最后判断边数是否大于该分量中的
点数,若是,将该分量边数的总和加到ans中,最终ans就是答案;
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
const int maxn = 10000 + 5;
struct Edge
{
int u,v;
};
int iscut[maxn],pre[maxn],bccno[maxn],dfs_clock,bcc_cnt,bridge,ans;
vector<int>G[maxn] , bcc[maxn];
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 = { u , v};
if(!pre[v]){
S.push(e);
child++;
int lowv = dfs(v , u);
lowu = min(lowv , lowu);
if(lowv >= pre[u]){
if(lowv > pre[u]) ++bridge; //判断是否是桥
iscut[u] = 1;
bcc_cnt++; bcc[bcc_cnt].clear();
int add = 0; //add含义:当前BCC中的边数;
for(;;){
Edge x = S.top(); S.pop();
++add; //去一条边,add就加一;
if(bccno[x.u]!=bcc_cnt){
bcc[bcc_cnt].push_back(x.u);
bccno[x.u] = bcc_cnt;
}
if(bccno[x.v]!=bcc_cnt){
bcc[bcc_cnt].push_back(x.v);
bccno[x.v] = bcc_cnt;
}
if(x.u==u &&x.v == v){
break;
}
}
if(add > bcc[bcc_cnt].size()) //该BCC中边数大于点数,ans加上add
ans += add;
}
}
else if(pre[v]<pre[u] && v != fa){
S.push(e);
lowu = min(lowu , pre[v]);
}
}
if(fa < 0 &&child == 1) iscut[u] = 0;
return lowu;
}
void find_bcc(int n)
{
mem(pre,0);
mem(bccno, 0);
mem(iscut , 0);
while(!S.empty()) S.pop();
dfs_clock = bcc_cnt = 0;
for(int i = 1;i<=n;i++)
if(!pre[i]) dfs(i , -1);
}
int main()
{
int n , m;
while(scanf("%d %d",&n,&m)!=EOF){
if(!n&&!m)
break;
bridge = 0;
ans = 0;
int x , y ;
for(int i = 0 ;i<=n;i++) G[i].clear();
for(int i = 0;i<=n;i++) bcc[i].clear();
for(int i = 0;i<m;i++){
scanf("%d %d",&x,&y);
G[x+1].push_back(y+1);
G[y+1].push_back(x+1);
}
find_bcc(n);
printf("%d %d\n",bridge , ans);
}
return 0;
}