1. 程式人生 > >[HNOI2012]礦場搭建

[HNOI2012]礦場搭建

isp names noi2012 des hid 設置 style alt space

明明是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     freopen("bzoj_2730.in","r",stdin);
59     freopen("bzoj_2730.out","w",stdout);
60     int n,k=0;
61     while(scanf("%d",&n)&&n){
62         init();int t=0;k++;
63         while(n--){
64             int a,b;
65             scanf("%d%d",&a,&b);
66             t=max(t,a);t=max(t,b);
67             G[a].push_back(b);
68             G[b].push_back(a);
69         }
70         for(int i=1;i<=t;i++)
71         if(!dfn[i])dfs(i,-1);
72         long long ans1=0,ans2=1;
73         for(int i=1;i<=cnt;i++){
74             int c=0;
75             for(int j=0;j<bcc[i].size();j++)
76             if(iscut[bcc[i][j]])c++;
77             if(c==1){
78                 ans1++;
79                 ans2*=bcc[i].size()-1;
80             }
81         }
82         if(cnt==1){
83             ans1=2;ans2=bcc[1].size()*(bcc[1].size()-1)/2;
84         }
85         printf("Case %d: %lld %lld\n",k,ans1,ans2);
86     }
87 }
View Code

[HNOI2012]礦場搭建