#include<iostream> #include<cstdio> using namespace std; struct Graph{ static const int maxn=1e5+5, maxm=3e5+5; struct star{int v,nex;}edge[maxm<<1]; int head[maxn],cnt,n; void ini(int n){ this->n=n; cnt=-1; for(int i=0;i<=n;i++) head[i]=-1; } void add_edge(int u,int v){ edge[++cnt]=star{v,head[u]}; head[u]=cnt; edge[++cnt]=star{u,head[v]}; head[v]=cnt; } }tree; struct Tarjan:Graph{//割点 int low[maxn],dfn[maxn],stk[maxn],cut[maxn]; int step; void tarjan(){ step=0; for(int i=0;i<=n;i++) dfn[i]=cut[i]=0; for(int i=1;i<=n;i++) if(dfn[i]==0) tarjan(i,0);//多个联通快 } void tarjan(int u,int father=0){//此函数不开放 low[u]=dfn[u]=++step; stk[++stk[0]]=u; int first=1, son=0; for(int i=head[u];~i;i=edge[i].nex){ int v=edge[i].v; if(v==father&&!first) first=false; else if(dfn[v]) low[u]=min(low[u],dfn[v]); else{ son++; tarjan(v,u); low[u]=min(low[u],low[v]); //一个顶点u是割点,当且仅当1或2 //1.u为树根且u有多与一个子树 //2.u不为树根且存在边(u,v)为树边,使得dfn[u]<=low[v] if(father!=0&&dfn[u]<=low[v]) cut[u]=1; if(father==0&&son>1) cut[u]=1; } } stk[0]--; } long long nums,cutnums,ans1,ans2; void solve(int Case){ tarjan(); ans1=0,ans2=1; for(int i=1;i<=n;i++) dfn[i]=0; for(int i=1;i<=n;i++) { if(!cut[i]&&dfn[i]==0) { nums=cutnums=0; dfs(i,i); if(nums==1);//do nothing else{ if(cutnums>=2) ;//do nothing else if(cutnums==1){ ans1++; ans2*=nums-1; } else{ ans1+=2; ans2*=(nums-1)*nums/2; } } } } cout<<"Case "<<Case<<": "<<ans1<<" "<<ans2<<endl;//输出结果 } void dfs(int u,int color){ nums++; cutnums+=cut[u]; dfn[u]=color; if(cut[u]) return; for(int i=head[u];~i;i=edge[i].nex){ int v=edge[i].v; if(dfn[v]!=color) dfs(v,color); } } }graph; inline int read(){ int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } int main(){ int t=0; while(true){ int m=read(),n=0; if(m==0) break; graph.ini(1000); for(int i=0;i<m;i++) { int u=read(),v=read(); graph.add_edge(u,v); n=max(u,n); n=max(v,n); } graph.n=n; graph.solve(++t); } }