• [BZOJ] 2730: [HNOI2012]矿场搭建


    2730: [HNOI2012]矿场搭建

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 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

    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

    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
    ",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 }
    qwq
  • 相关阅读:
    HDU 3848 CC On The Tree 树形DP
    编程求取直线一般式表达式,两直线交点
    向外国学者所要论文源代码--英语模版
    找出该树中第二小的值--思路及算法实现
    不使用额外空间交换2个数据的源代码
    华为2018软件岗笔试题解题思路和源代码分享
    华为笔试题--LISP括号匹配 解析及源码实现
    Linux 快捷键汇总(偏基础)
    快速排序算法思路分析和C++源代码(递归和非递归)
    Python读取SQLite文件数据
  • 原文地址:https://www.cnblogs.com/Chorolop/p/7635166.html
Copyright © 2020-2023  润新知