BZOJ 2730:[HNOI2012]矿场搭建(割点+连通块)
[HNOI2012]矿场搭建
Description
煤矿工地可以看成是由隧道连接挖煤点组成的无向图。为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
Input
输入文件有若干组数据,每组数据的第一行是一个正整数 N(N≤500),表示工地的隧道数,接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖 S 与挖煤点 T 由隧道直接连接。输入数据以 0 结尾。
Output
输入文件中有多少组数据,输出文件 output.txt 中就有多少行。每行对应一组输入数据的 结果。其中第 i 行以 Case i: 开始(注意大小写,Case 与 i 之间有空格,i 与:之间无空格,: 之后有空格),其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需 要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总 数。输入数据保证答案小于 2^64。输出格式参照以下输入输出样例。
Sample Input
9
1 3
4 1
3 5
1 2
2 6
1 5
6 3
1 6
3 2
6
1 2
1 3
2 4
2 5
3 6
3 7
0
Sample Output
Case 1: 2 4
Case 2: 4 1
HINT
Case 1 的四组解分别是(2,4),(3,4),(4,5),(4,6);
Case 2 的一组解为(4,5,6,7)。
Source
day1
分析:
显然坍塌的点是割点才有影响。
某个割点被删去后,会分成多个连通块,每个连通块都要安排一个出口,方案数显然为各连通块点数之积。
但会有很多割点,也就是每个割点都要满足上述条件。
可以考虑把所有割点删去后求连通块,将所有只连一个割点连通块的点数乘起来即为方案数。
如果整个图无割点,或所有点要么为割点,要么与多个割点相连,这两种情况最少出口数为2,方案为C(n,2)。
代码:
program build; type point=^node; node=record x:longint; next:point; end; var a:array[0..500]of point; dfn,low,r:array[0..500]of longint; mk,g:array[0..500]of boolean; n,i,m,s,num,j,x,y,tot:longint; s1,s2,ans:int64; procedure add(x,y:longint); var p:point; begin new(p); p^.x:=y; p^.next:=a[x]; a[x]:=p; end; function min(x,y:longint):longint; begin if x<y then min:=x else min:=y; end; procedure tarjan(x,fa:longint); var y,k:longint; p:point; begin inc(s); low[x]:=s; dfn[x]:=s; new(p); p:=a[x]; k:=0; while p<>nil do begin y:=p^.x; if dfn[y]=0 then begin inc(k); tarjan(y,x); low[x]:=min(low[x],low[y]); if low[y]>=dfn[x] then mk[x]:=true; end else if (dfn[y]<dfn[x])and(y<>fa) then low[x]:=min(low[x],dfn[y]); p:=p^.next; end; if (k=1)and(fa=-1) then mk[x]:=false; end; procedure dfs(x:longint); var y:longint; p:point; begin new(p);p:=a[x]; g[x]:=true; inc(s2); while p<>nil do begin y:=p^.x; if g[y]=false then if mk[y]=true then begin g[y]:=true; inc(s1); r[s1]:=y; end else dfs(y); p:=p^.next; end; end; begin num:=0; m:=1; while m<>0 do begin readln(m); if m=0 then break; inc(num); n:=0; for i:=1 to 500 do a[i]:=nil; for i:=1 to m do begin readln(x,y); if x>n then n:=x; if y>n then n:=y; add(x,y);add(y,x); end; for i:=1 to n do begin dfn[i]:=0; low[i]:=0; mk[i]:=false;g[i]:=false; end; s:=0; tot:=0; ans:=1; tarjan(1,-1); for i:=1 to n do if (not mk[i])and(not g[i]) then begin s1:=0; s2:=0; dfs(i); if s1=1 then begin inc(tot); ans:=ans*s2; end; for j:=1 to s1 do g[r[j]]:=false; end; if tot=0 then begin tot:=2; ans:=n*(n-1) div 2; end; writeln('Case ',num,': ',tot,' ',ans); end; end.