无向图的双连通分量-------矿场搭建

煤矿工地可以看成是由隧道连接挖煤点组成的无向图。
为安全起见,希望在工地发生事故时所有挖煤点的工人都能有一条出路逃到救援出口处。
于是矿主决定在某些挖煤点设立救援出口,使得无论哪一个挖煤点坍塌之后,其他挖煤点的工人都有一条道路通向救援出口。
请写一个程序,用来计算至少需要设置几个救援出口,以及不同最少救援出口的设置方案总数。
输入格式
输入文件有若干组数据,每组数据的第一行是一个正整数 N,表示工地的隧道数。
接下来的 N 行每行是用空格隔开的两个整数 S 和 T,表示挖煤点 S 与挖煤点 T 由隧道直接连接。
注意,每组数据的挖煤点的编号为 1 ~ Max,其中 Max 表示由隧道连接的挖煤点中,编号最大的挖煤点的编号,可能存在没有被隧道连接的挖煤点。
输入数据以 0 结尾。
输出格式
每组数据输出结果占一行。
其中第 i 行以 Case i: 开始(注意大小写,Case 与 i 之间有空格,i 与:之间无空格,: 之后有空格)。
其后是用空格隔开的两个正整数,第一个正整数表示对于第 i 组输入数据至少需要设置几个救援出口,第二个正整数表示对于第 i 组输入数据不同最少救援出口的设置方案总数。
输入数据保证答案小于 264264,输出格式参照以下输入输出样例。
数据范围
1≤N≤5001≤N≤500,

1≤Max≤10001≤Max≤1000
输入样例:
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

输出样例:
Case 1: 2 4
Case 2: 4 1


```#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef unsigned long long ULL;
const int N = 1010, M = 1010;
int n, m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp;
int stk[N], top;
int  dcc_cnt;
vector<int> dcc[N];
bool cut[N];
int root;
void add(int a, int b){
 e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void tarjan(int u){
 dfn[u] = low[u] = ++ timestamp;
 stk[++ top] = u;
  if (u == root && h[u] == -1){
  dcc_cnt ++, dcc[dcc_cnt].push_back(u);
  return ;
 }
  int cnt = 0;
 for (int i = h[u]; ~i; i = ne[i]){
  int j = e[i];
  if (!dfn[j]){
   tarjan(j);
   low[u] = min(low[u], low[j]);
   if (dfn[u] <= low[j]){
    cnt ++;
    if (u != root || cnt > 1)    cut[u] = true;
    ++ dcc_cnt;
    int y;
    do{
     y = stk[top --];
     dcc[dcc_cnt].push_back(y);
    }while(y != j);
    dcc[dcc_cnt].push_back(u);
   }
  }
  else       low[u] = min(low[u], dfn[j]);
 }
}
int main(){
 int T = 1;
 while(cin >> m, m){
  for (int i = 1; i <= dcc_cnt; i ++)   dcc[i].clear();
  idx = n = timestamp = top = dcc_cnt = 0;
  memset(h, -1, sizeof h);
  memset(dfn, 0, sizeof dfn);
  memset(cut, 0, sizeof cut);
    while(m --){
   int a, b;
   cin >> a >> b;
   n = max(a, n),  n = max(n, b);
   add(a, b),   add(b, a); 
  }
    for (root = 1; root <= n; root ++)
     if (!dfn[root])
      tarjan(root);
        int res = 0;
  ULL num = 1;
  for (int i = 1; i <= dcc_cnt; i ++){
   int cnt = 0;
   for (int j = 0; j < dcc[i].size(); j ++)
      if (cut[dcc[i][j]])
        cnt ++;
           if (cnt == 0){
    if (dcc[i].size() > 1)    res += 2, num *= dcc[i].size() * (dcc[i].size() - 1) / 2;
    else  res ++;
   }
   else if (cnt == 1)   res ++, num *= dcc[i].size() - 1;
  }
    printf("Case %d: %d %llu\n", T ++, res, num);
 }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值