UVALive - 5135 - Mining Your Own Business(双连通分量+思维)
Time Limit: 5000 mSec
Problem Description
John Digger is the owner of a large illudium phosdex mine. The mine is made up of a series of tunnels that meet at various large junctions. Unlike some owners, Digger actually cares about the welfare of his workers and has a concern about the layout of the mine. Specifically, he worries that there may a junction which, in case of collapse, will cut off workers in one section of the mine from other workers (illudium phosdex, as you know, is highly unstable). To counter this, he wants to install special escape shafts from the junctions to the surface. He could install one escape shaft at each junction, but Digger doesn’t care about his workers that much. Instead, he wants to install the minimum number of escape shafts so that if any of the junctions collapses, all the workers who survive the junction collapse will have a path to the surface. Write a program to calculate the minimum number of escape shafts and the total number of ways in which this minimum number of escape shafts can be installed.
Input
The input consists of several test cases. The first line of each case contains a positive integer N (N ≤ 5·104) indicating the number of mine tunnels. Following this are N lines each containing two distinct integers s and t, where s and t are junction numbers. Junctions are numbered consecutively starting at 1. Each pair of junctions is joined by at most a single tunnel. Each set of mine tunnels forms one connected unit (that is, you can get from any one junction to any other). The last test case is followed by a line containing a single zero.
Output
Sample Input
Sample Output
Case 1: 2 4
Case 2: 4 1
题解:做这种题就是涨姿势,删掉一个点之后还能连通,对于无向图来说,双连通才能满足这条性质,因此着眼点就放在了双连通分量上。接下来通过点双连通分量缩点来考虑问题。因为一个割点可能属于多个v-BCC,设图中有p个割点和t个v-BCC,我们建立一张包含p+t个节点的新图,把每个v-BCC和每个割点都作为新图中的节点,并在每个割点与包含它的所有v-BCC之间连边。这张图就变成了一棵树或是一片森林。我们可以只考虑树的情况,因为森林中的树相互之间独立,方案数满足乘法原理。随便找个节点转成有根树,则所有叶子节点都是v-BCC(如果叶子节点是割点那把他删去并不会使连通分量数增加),很明显这些节点中都需要一个太平井,之后可以证明除了这些节点需要太平井之外,别的节点都不需要。首先割点节点都是不需要的,因为如果该节点连接的两个双连通分量都只依靠该节点的太平井,那么删去该点之后这两个双连通分量还需要两个太平井,而如果有了这两个太平井,显然割点处的太平井是不需要的,其次,除了叶节点之外的双连通分量节点都至少连接两个割点,因此不论删去哪个割点,该分量节点所在的联通块都至少包含一个叶子节点,因此该节点不需要太平井。之后就是乘法原理了,有个需要注意的地方就是如果整个图是一个BCC,之前的分析就不奏效了,需要重新考虑,不过这种情况很简单,想到了就没问题。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define REP(i, n) for (int i = 1; i <= (n); i++) 6 #define sqr(x) ((x) * (x)) 7 8 const int maxn = 50000 + 10; 9 const int maxm = 30 + 10; 10 const int maxs = 10000 + 10; 11 12 typedef long long LL; 13 typedef pair<int, int> pii; 14 typedef pair<double, double> pdd; 15 16 const LL unit = 1LL; 17 const int INF = 0x3f3f3f3f; 18 const LL mod = 1000000007; 19 const double eps = 1e-14; 20 const double inf = 1e15; 21 const double pi = acos(-1.0); 22 23 struct Edge 24 { 25 int u, v; 26 }; 27 28 int n, m; 29 vector<int> G[maxn]; 30 int dfs_clock, bcc_cnt; 31 int pre[maxn], is_cut[maxn], bccno[maxn]; 32 vector<int> bcc[maxn]; 33 stack<Edge> S; 34 35 int dfs(int u, int fa) 36 { 37 int lowu = pre[u] = ++dfs_clock; 38 int child = 0; 39 for (auto v : G[u]) 40 { 41 Edge e = (Edge){u, v}; 42 if (!pre[v]) 43 { 44 S.push(e); 45 child++; 46 int lowv = dfs(v, u); 47 lowu = min(lowu, lowv); 48 if (lowv >= pre[u]) 49 { 50 is_cut[u] = 1; 51 bcc_cnt++; 52 bcc[bcc_cnt].clear(); 53 for (;;) 54 { 55 Edge x = S.top(); 56 S.pop(); 57 if (bccno[x.u] != bcc_cnt) 58 { 59 bcc[bcc_cnt].push_back(x.u); 60 bccno[x.u] = bcc_cnt; 61 } 62 if (bccno[x.v] != bcc_cnt) 63 { 64 bcc[bcc_cnt].push_back(x.v); 65 bccno[x.v] = bcc_cnt; 66 } 67 if (x.u == u && x.v == v) 68 { 69 break; 70 } 71 } 72 } 73 } 74 else if (pre[v] < pre[u] && v != fa) 75 { 76 S.push(e); 77 lowu = min(lowu, pre[v]); 78 } 79 } 80 if (fa < 0 && child == 1) 81 { 82 is_cut[u] = 0; 83 } 84 return lowu; 85 } 86 87 void find_bcc() 88 { 89 memset(pre, 0, sizeof(pre)); 90 memset(is_cut, 0, sizeof(is_cut)); 91 memset(bccno, 0, sizeof(bccno)); 92 dfs_clock = bcc_cnt = 0; 93 for (int i = 0; i < n; i++) 94 { 95 if (!pre[i]) 96 { 97 dfs(i, -1); 98 } 99 } 100 } 101 102 int iCase; 103 104 int main() 105 { 106 ios::sync_with_stdio(false); 107 cin.tie(0); 108 freopen("input.txt", "r", stdin); 109 //freopen("output.txt", "w", stdout); 110 while (cin >> m && m) 111 { 112 for (int i = 0; i < maxn; i++) 113 { 114 G[i].clear(); 115 } 116 n = 0; 117 int u, v; 118 for (int i = 0; i < m; i++) 119 { 120 cin >> u >> v; 121 u--, v--; 122 G[u].push_back(v); 123 G[v].push_back(u); 124 n = max(n, u); 125 n = max(n, v); 126 } 127 find_bcc(); 128 LL ans1 = 0, ans2 = 1LL; 129 for (int i = 1; i <= bcc_cnt; i++) 130 { 131 int cnt = 0; 132 for (auto v : bcc[i]) 133 { 134 if (is_cut[v]) 135 cnt++; 136 } 137 if (cnt == 1) 138 { 139 ans1++; 140 ans2 *= (LL)bcc[i].size() - 1; 141 } 142 } 143 if (bcc_cnt == 1) 144 { 145 ans1 = 2; 146 ans2 *= (LL)bcc[1].size() * (bcc[1].size() - 1) / 2; 147 } 148 cout << "Case " << ++iCase << ": " << ans1 << " " << ans2 << endl; 149 } 150 return 0; 151 }