BZOJ2730:[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)。

Solution

首先我们先跑一遍tarjan将割点找出来。
既然要炸肯定要炸割点,炸非割点是无法产生影响的,我们我们只考虑割点。
将原图中的割点去掉,得到的新图会是若干个连通块
若一个连通块靠近两个割点,那么这个连通块上是不需要建立的,因为炸了一个割点可以往另一个跑
若一个连通块只靠近一个割点,那么这个连通块必须建一个,不然炸了这个唯一的割点岂不是就GG了
注意考虑没有割点的情况,这种情况只需要随便建立两个即可。

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #define N (1001)
 6 using namespace std;
 7 
 8 struct Edge{int from,to,next;}edge[N<<2];
 9 int head[N],num_edge;
10 int Dfn[N],Low[N],dfs_num;
11 int n,Cut[N],a[N],cnt,u,v,vis[N];
12 int cut_num,size,case_num,num;
13 long long ans1,ans2;
14 
15 void add(int u,int v)
16 {
17     edge[++num_edge].to=v;
18     edge[num_edge].from=u;
19     edge[num_edge].next=head[u];
20     head[u]=num_edge;
21 }
22 
23 void Tarjan(int x,int fa)
24 {
25     long long son_num=0,sum=0;
26     Dfn[x]=Low[x]=++dfs_num;
27     for (int i=head[x]; i; i=edge[i].next)
28         if (!Dfn[edge[i].to])
29         {
30             son_num++;
31             Tarjan(edge[i].to,x);
32             Low[x]=min(Low[x],Low[edge[i].to]);
33             if (Low[edge[i].to]>=Dfn[x]) Cut[x]=true,cut_num++;
34         }
35         else if (Dfn[x]>Dfn[edge[i].to] && edge[i].to!=fa)
36             Low[x]=min(Low[x],Dfn[edge[i].to]);
37     if (fa==0 && son_num==1)
38         Cut[x]=false,cut_num--;
39 }
40 
41 void Dfs(int x)
42 {
43     vis[x]=true;
44     if (Cut[x]){num++; return;}
45     size++;
46     for (int i=head[x]; i; i=edge[i].next)
47         if (!vis[edge[i].to])
48             Dfs(edge[i].to);
49 }
50 
51 int main()
52 {
53     scanf("%d",&n);
54     while (n!=0)
55     {
56         memset(edge,0,sizeof(edge)); memset(Dfn,0,sizeof(Dfn));
57         memset(head,0,sizeof(head)); memset(Low,0,sizeof(Low));
58         memset(Cut,0,sizeof(Cut));   memset(vis,0,sizeof(vis));
59         num_edge=0; cnt=0; dfs_num=0; cut_num=0;
60 
61         for (int i=1; i<=n; ++i)
62         {
63             scanf("%d%d",&u,&v);
64             add(u,v); add(v,u);
65             a[++cnt]=u; a[++cnt]=v;
66         }
67         sort(a+1,a+cnt+1);
68         n=unique(a+1,a+cnt+1)-a-1;
69 
70         for (int i=1; i<=n; ++i)
71             if (!Dfn[a[i]])
72                 Tarjan(a[i],0);
73         
74         if (cut_num==0)
75         {
76             case_num++;
77             printf("Case %d: 2 %lld\n",case_num,(long long)n*(n-1)/2);
78             scanf("%d",&n);
79             continue;
80         }
81         ans1=0,ans2=1;
82         for (int i=1; i<=n; ++i)
83             if (!vis[a[i]] && !Cut[a[i]])
84             {
85                 num=0; size=0;
86                 Dfs(a[i]);
87                 for (int j=1; j<=n; ++j)
88                     if (Cut[j])
89                         vis[j]=false;
90                 if (num>1) continue;
91                 ans1++; ans2*=size;
92             }
93         case_num++;
94         printf("Case %d: %lld %lld\n",case_num,ans1,ans2);
95         scanf("%d",&n);
96     }
97 }
posted @ 2018-07-10 19:10  Refun  阅读(195)  评论(0编辑  收藏  举报