[bzoj2730][HNOI2012]矿场搭建
明明是Acm World Finals 2011的原题,把数据范围改小了就放出来了?!这样不好,,
昨天讲完双连通分量今天A一道点双的题。
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)。
先跑一遍点双,缩一下点。网上有删点的做法没看懂。
思路如下:
对于只有一个割点的连通分量,里面至少要有一个救援出口。因为一旦割点断了,该分量被孤立,一定要有一个出口。
如果一个点双里有不止一个割点,说明啥?一个割点断了不影响啥,一定能通过另一个割点到达另一个点双。
还有,割点不要去涂。题面要求救援出口尽量少,如果放在割点,割点断了点双里剩下的咋办?就还要建,不合适了。
特判:全图点双连通无割点,随便取两个点一个坏了还有另一个就行了。此时答案C(n,2)=n(n-1)/2。
重点在于理解割点的含义,知道一个割点可能属于多个点双连通分量,一个点双连通分量只要不是全图就一定有至少一个割点。在跑Tarjan时割点都会被存进bcc的vector里,只要判断每个bcc里割点数是否为1,再利用乘法原理求解ans2即可。附代码。
1 #include<cstdio>
2 #include<iostream>
3 #include<cmath>
4 #include<vector>
5 #include<stack>
6 #include<cstring>
7 #define mxn 510
8 using namespace std;
9 struct Edge{
10 int from,to;
11 };
12 int dfn[mxn],iscut[mxn],belong[mxn];
13 int dfs_clock=0,cnt=0;
14 vector<int> G[mxn],bcc[mxn];
15 stack<Edge>s;
16 int dfs(int u,int fa){
17 int lowu=dfn[u]=++dfs_clock;
18 int child=0;
19 for(int i=0;i<G[u].size();i++){
20 int v=G[u][i];
21 Edge e=(Edge){u,v};
22 if(!dfn[v]){
23 s.push(e);child++;
24 int lowv=dfs(v,u);
25 lowu=min(lowu,lowv);
26 if(lowv>=dfn[u]){
27 iscut[u]=true;bcc[++cnt].clear();
28 while(true){
29 Edge x=s.top();s.pop();
30 if(belong[x.from]!=cnt){
31 bcc[cnt].push_back(x.from);
32 belong[x.from]=cnt;
33 }
34 if(belong[x.to]!=cnt){
35 bcc[cnt].push_back(x.to);
36 belong[x.to]=cnt;
37 }
38 if(x.from==u&&x.to==v) break;
39 }
40 }
41 }
42 else if(dfn[v]<dfn[u]&&v!=fa){
43 s.push(e);lowu=min(lowu,dfn[v]);
44 }
45 }
46 if(fa<0&&child==1) iscut[u]=0;
47 return lowu;
48 }
49 void init(){
50 memset(belong,0,sizeof(belong));
51 memset(dfn,0,sizeof(dfn));
52 memset(iscut,0,sizeof(iscut));
53 for(int i=1;i<mxn;i++)
54 G[i].clear();
55 dfs_clock=cnt=0;
56 }
57 int main(){
58 int n,k=0;
59 while(scanf("%d",&n)&&n){
60 init();int t=0;k++;
61 while(n--){
62 int a,b;
63 scanf("%d%d",&a,&b);
64 t=max(t,a);t=max(t,b);
65 G[a].push_back(b);
66 G[b].push_back(a);
67 }
68 for(int i=1;i<=t;i++)
69 if(!dfn[i])dfs(i,-1);
70 long long ans1=0,ans2=1;
71 for(int i=1;i<=cnt;i++){
72 int c=0;
73 for(int j=0;j<bcc[i].size();j++)
74 if(iscut[bcc[i][j]])c++;
75 if(c==1){
76 ans1++;
77 ans2*=bcc[i].size()-1;
78 }
79 }
80 if(cnt==1){
81 ans1=2;ans2=bcc[1].size()*(bcc[1].size()-1)/2;
82 }
83 printf("Case %d: %lld %lld\n",k,ans1,ans2);
84 }
85 }