题目大意:有N个矿井 ,由一些隧道连接起来,现在要修建尽量少的安全通道,使得无论哪里发生事故,所有人均能逃出,求建的最少的安全通道数量和方案数
解题思路:建安全通道的话,肯定不能建在割顶,因为割顶如果崩塌了,割顶所连接的双连通分量内的点就跑不掉了,还得在双连通分量里面再建点(上述为双连通分量内部只有一个割顶的情况),这样不划算,还不如直接在里面建点
如果一个双连通分量的内部割顶有多个的话,那么在这个双连通分量里面就可以不用建安全通道了,因为一个割顶崩塌了,还有其他点可以连向外面,所以,只考虑内部只有一个割顶的双连通分量要怎么建安全通道
怎么建呢,排除掉割顶的所有的点都可以建
假设ans1代表所需要建立的安全通道的数量,ans2代表建立的方案数,那么找到内部只有一个割顶的双连通分量时(双连通分量的内部结点有n个,排除掉割顶的话,还有n-1种建立的方案)
那么ans1 = ans1 + 1; ans2 *= (n - 1)
如果给的图形成的是一个双连通分量(假设有n个点),只有一个,那么需要建立2个安全通道(一个倒塌了,还有另一个),方案数为n * (n - 1) / 2
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
#include <stack>
using namespace std;
#define N 100010
#define INF 0x3f3f3f3f
#define ll long long
struct Edge{
int to, next;
}E[N];
struct Node {
int u, v;
Node() {}
Node(int u, int v): u(u), v(v) {}
};
int pre[N], iscut[N], bccno[N], head[N], dfs_clock, bcc_cnt;
int n, tot, Max;
vector<int> bcc[N];
stack<Node> S;
void AddEdge(int from, int to) {
E[tot].to = to;
E[tot].next = head[from];
head[from] = tot++;
}
void init() {
memset(head, -1, sizeof(head));
tot = 0;
int u, v;
Max = -INF;
for (int i = 0; i < n; i++) {
scanf("%d%d", &u, &v);
u--; v--;
AddEdge(u, v);
AddEdge(v, u);
if (u > Max || v > Max) {
Max = max(u, v);
}
}
}
int dfs(int u, int fa) {
int lowu = pre[u] = ++dfs_clock;
int child = 0;
for (int i = head[u]; i != -1; i = E[i].next) {
int v = E[i].to;
Node e = Node(u, v);
if (!pre[v]) {
S.push(e);
child++;
int lowv = dfs(v, u);
lowu = min(lowu, lowv);
if (lowv >= pre[u]) {
iscut[u] = true;
bcc_cnt++;
bcc[bcc_cnt].clear();
while (1) {
Node t = S.top();
S.pop();
if (bccno[t.u] != bcc_cnt) {
bcc[bcc_cnt].push_back(t.u);
bccno[t.u] = bcc_cnt;
}
if (bccno[t.v] != bcc_cnt) {
bcc[bcc_cnt].push_back(t.v);
bccno[t.v] = bcc_cnt;
}
if (t.u == u && t.v == v)
break;
}
}
}
else if(pre[v] < pre[u] && v != fa) {
lowu = min(pre[v], lowu);
S.push(e);
}
}
if (fa < 0 && child == 1)
iscut[u] = false;
return lowu;
}
int cas = 1;
void solve() {
memset(pre, 0, sizeof(pre));
memset(iscut, 0, sizeof(iscut));
memset(bccno, 0, sizeof(bccno));
dfs_clock = bcc_cnt = 0;
for (int i = 0; i <= Max; i++)
if (!pre[i])
dfs(i, -1);
ll ans1 = 0, ans2 = 1;
for (int i = 1; i <= bcc_cnt; i++) {
int cnt_cut = 0;
for (int j = 0; j < bcc[i].size(); j++) {
if (iscut[bcc[i][j]])
cnt_cut++;
}
if (cnt_cut == 1) {
ans1++;
ans2 *= (ll) (bcc[i].size() - 1);
}
}
if (bcc_cnt == 1) {
ans1 = 2;
ans2 = (ll)bcc[1].size() * (bcc[1].size() - 1) / 2;
}
printf("Case %d: %lld %lld\n", cas++, ans1, ans2);
}
int main() {
while(scanf("%d", &n) != EOF && n) {
init();
solve();
}
return 0;
}