[BZOJ] 2730: [HNOI2012]矿场搭建
2730: [HNOI2012]矿场搭建
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2317 Solved: 1076
[Submit][Status][Discuss]
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
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
Case 2: 4 1
HINT
Case 1 的四组解分别是(2,4),(3,4),(4,5),(4,6);
Case 2 的一组解为(4,5,6,7)。
Source
Analysis
qwq调个水题调错了近十次,,,
那么我们根据数据可以建图,这个图我们可以得出这些性质:
1. 割点不是出口,如果割点是出口的话,塌掉好像,,,就塌掉了,不是最优解。正解应该是在点双连通分量里建一个出口。
2. 有两个割点的连通块就没必要建出口了:因为可以跑到两个割点相连接的其他连通块
3. 没有割点的连通块至少有两个出口:万一塌掉一个出口,,,,
第三点尤为重要。
那么解法就是:
首先求双连通分量顺便把割点数目计算出来
然后把每个连通块内建出口的方案乘起来
注意只有一个割点和无割点的情况才需要计算
还有一件事,n不是点的数量
还有一件事,初始化
还有一件事,需要输出 Case %d:
Code
1 #include<cstdio> 2 #include<iostream> 3 #include<vector> 4 #include<stack> 5 #include<cstring> 6 #define maxn 600 7 using namespace std; 8 9 int dfn[maxn],TIM,size[maxn],bcc_cnt,iscut[maxn],bccno[maxn],n,buck[maxn],m; 10 11 struct edge{ int from; int u; int v; }e[maxn]; int tot,first[maxn]; 12 void insert(int u,int v){ tot++; e[tot].from = first[u]; e[tot].u = u; e[tot].v = v; first[u] = tot; } 13 14 vector<int> bcc[maxn]; 15 stack<edge> S; 16 17 int dfs(int u,int fa){ 18 int lowu = dfn[u] = ++TIM; 19 int child = 0; 20 for(int i = first[u];i;i = e[i].from){ 21 int v = e[i].v; 22 edge e = (edge){0,u,v}; 23 if(!dfn[v]){ 24 S.push(e); 25 child++; 26 int lowv = dfs(v,u); 27 lowu = min(lowu,lowv); 28 if(lowv >= dfn[u]){ 29 iscut[u] = true; 30 bcc_cnt++; bcc[bcc_cnt].clear(); 31 for(;;){ 32 edge x = S.top();S.pop(); 33 if(bccno[x.u] != bcc_cnt){ 34 if(bccno[x.u]) buck[bccno[x.u]]++; 35 bcc[bcc_cnt].push_back(x.u); bccno[x.u] = bcc_cnt; 36 } 37 if(bccno[x.v] != bcc_cnt){ 38 if(bccno[x.v]) buck[bccno[x.v]]++; 39 bcc[bcc_cnt].push_back(x.v); bccno[x.v] = bcc_cnt; 40 } 41 if(x.u == u && x.v == v) break; 42 } 43 } 44 }else if(dfn[v] < dfn[u] && v != fa) { 45 S.push(e); lowu = min(lowu,dfn[v]); 46 } 47 }if(fa < 0 && child == 1) iscut[u] = false; 48 return lowu; 49 } 50 51 void Build(){ 52 memset(dfn,0,sizeof(dfn)); 53 memset(iscut,0,sizeof(iscut)); 54 memset(bccno,0,sizeof(bccno)); 55 memset(buck,0,sizeof(buck)); 56 TIM = bcc_cnt = 0; 57 58 for(int i = 1;i <= n;i++) 59 if(!dfn[i]) dfs(i,-1); 60 } 61 62 int main(){ 63 int T = 1; 64 scanf("%d",&m); 65 while(m){ 66 n = 0; 67 for(int i = 1;i <= m;i++){ 68 int x,y; scanf("%d%d",&x,&y); 69 n = max(n,max(x,y)); 70 insert(x,y); insert(y,x); 71 }Build(); 72 for(int i = 1;i <= n;i++) 73 if(iscut[i]) buck[bccno[i]]++; 74 75 long long ans = 1; 76 int tot = 0; 77 for(int i = 1;i <= bcc_cnt;i++){ 78 if(buck[i] == 1) ans *= max(((int)bcc[i].size()-1),1),tot++; 79 if(!buck[i]) ans *= bcc[i].size()*(bcc[i].size()-1)/2,tot += 2; 80 // cout << "ans" << ans << endl; 81 } 82 printf("Case %d: %d %lld\n",T,tot,ans); T++; 83 // cout << ans << endl; 84 // cout << "buck: "; 85 // for(int i = 1;i <= n;i++) printf("%d ",buck[i]); cout << endl; 86 // cout << "bcc: "; 87 // for(int i = 1;i <= bcc_cnt;i++) printf("%d ",bcc[i].size()); cout << endl; 88 memset(first,0,sizeof(first)); 89 tot = 0; 90 scanf("%d",&m); 91 }return 0; 92 }
转载请注明出处 -- 如有意见欢迎评论