题目:
题意:
给出n组数(u,v),表示u与v点通过隧道相连, 要选择最少的点安装安全井,使得任意点倒塌的时候矿工能够逃脱.
思路:
题目转化为,在无向图中任意选择最少的点涂黑,是的任意删除一个点后,每一个连通分量至少有一个黑点.
所以不应该把割顶涂黑. 且在同一个连通分量中只需要涂一个点.
一种特殊情况是整个图没有割顶的话应该选择两个点涂.
AC.
#include <iostream>
#include <cstdio>
#include <vector>
#include <stack>
#include <cstring>
using namespace std;
const int maxn = 5e4+5;
vector<int> G[maxn], bcc[maxn];
int pre[maxn], iscut[maxn], bccno[maxn], dfs_clock, bcc_cnt;
struct edge {
int u, v;
edge(int a, int b) {
u = a; v = b;
}
};
stack<edge> S;
int dfs(int u, int fa)
{
int lowu = pre[u] = ++dfs_clock;
int child = 0;
for(int i= 0; i < G[u].size(); ++i) {
int v = G[u][i];
edge e(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) {
edge x = S.top(); S.pop();
if(bccno[x.u] != bcc_cnt) {
bcc[bcc_cnt].push_back(x.u);
bccno[x.u] = bcc_cnt;
}
if(bccno[x.v] != bcc_cnt) {
bcc[bcc_cnt].push_back(x.v);
bccno[x.v] = bcc_cnt;
}
if(x.u == u && x.v == v) break;
}
}
}
else if(pre[v] < pre[u] && v != fa) {
S.push(e);
lowu = min(lowu, pre[v]);
}
}
if(fa < 0 && child == 1) iscut[u] = 0;
return lowu;
}
void find_bcc(int n)
{
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 < n; ++i) {
if(!pre[i]) dfs(i, -1);
}
}
void cal()
{
long long ans1 = 0, ans2 = 1;
for(int i = 1; i <= bcc_cnt; ++i) {
int cnt = 0;
for(int j = 0; j < bcc[i].size(); ++j) {
if(iscut[bcc[i][j]]) cnt++;
}
if(cnt == 1) {
ans1++;
ans2 *= (long long)(bcc[i].size()-cnt);
}
}
if(bcc_cnt == 1) {
ans1 = 2;
ans2 = bcc[1].size()*(bcc[1].size() - 1) / 2;
}
printf(" %lld %lld\n", ans1, ans2);
}
int main()
{
//freopen("in", "r", stdin);
int n;
int ca = 1;
while(~scanf("%d", &n)) {
if(n == 0) break;
for(int i = 0; i < maxn; ++i) G[i].clear();
for(int i = 0; i < n; ++i) {
int u, v;
scanf("%d %d", &u, &v);
u--; v--;
G[u].push_back(v);
G[v].push_back(u);
}
find_bcc(n);
printf("Case %d:", ca++);
cal();
}
return 0;
}