首先不难想到要先求割顶,求割顶的方法白书上有讲解
由于是一个矿崩塌,所以假如一个连通块连接了两个以上割顶,那么这个连通块内显然是不用设出口的
连接块只连接了一个割顶,那么出口可以设在这个连通块内任意位置
由此我们可以把两个问题解决了
注意特判一种情况,当不存在割顶的时候,至少要设两个出口

 type node=record
po,next:longint;
end; var w:array[..] of node;
dfn,low,mark,cs,s,p:array[..] of longint;
v,cut:array[..] of boolean;
task,tot,t,i,x,y,len,n,m:longint;
ans:int64; function min(a,b:longint):longint;
begin
if a>b then exit(b) else exit(a);
end; procedure add(x,y:longint);
begin
inc(len);
w[len].po:=y;
w[len].next:=p[x];
p[x]:=len;
end; procedure dfs(x,fa:longint);
var i,y,ch:longint;
begin
inc(t);
dfn[x]:=t;
low[x]:=t;
i:=p[x];
ch:=;
while i<>- do
begin
y:=w[i].po;
if dfn[y]= then
begin
dfs(y,x);
inc(ch);
low[x]:=min(low[x],low[y]);
if low[y]>=dfn[x] then cut[x]:=true; //只要有子树不能连回当前点的祖先
end
else if (dfn[y]<dfn[x]) and (fa<>y) then //注意fa<>y
low[x]:=min(low[x],dfn[y]);
i:=w[i].next;
end;
if (fa<) and (ch=) then cut[x]:=false; //注意单链的首位不是割顶
end; procedure color(x:longint);
var i,y:longint;
begin
i:=p[x];
v[x]:=true;
inc(s[t]);
while i<>- do
begin
y:=w[i].po;
if not v[y] then
begin
if not cut[y] then color(y)
else if mark[y]<>t then
begin
mark[y]:=t;
inc(cs[t]);
end;
end;
i:=w[i].next;
end;
end; begin
readln(m);
while m<> do
begin
inc(task);
len:=-;
fillchar(p,sizeof(p),);
n:=;
for i:= to m do
begin
readln(x,y);
add(x,y);
add(y,x);
if x>n then n:=x;
if y>n then n:=y;
end;
fillchar(dfn,sizeof(dfn),);
fillchar(cut,sizeof(cut),false);
for i:= to n do
if dfn[i]= then
begin
t:=;
dfs(i,-);
end;
ans:=;
t:=;
tot:=;
fillchar(v,sizeof(v),false);
fillchar(mark,sizeof(mark),);
fillchar(cs,sizeof(cs),);
for i:= to n do
if not v[i] and not cut[i] then
begin
inc(t);
s[t]:=;
color(i);
if cs[t]= then
begin
inc(tot);
ans:=ans*int64(s[t]);
end;
end;
if t= then //特判
begin
tot:=;
ans:=n*(n-) div ;
end;
writeln('Case ',task,': ',tot,' ',ans);
readln(m);
end;
end.
05-11 15:26