[HNOI2012]矿场搭建
题目传送门
分析:
这道题是跟$Tarjan$有关的一题。
如果某一个点双连通分量中没有割顶,则这个点双的答案为$2$。因为塌了任何一个点,其他任意两点仍然连通。防止塌的点就是救援口,所以要设置$2$个。
如果某一个点双连通分量中有一个割顶,说明至少有两个点双公用这一个割顶。所以必须保证每个点双中除割顶的其他任意一点是救援口,且只需一个。如果塌了割点,对每个点双无影响;如果塌了其中一个救援口,该点双能通过割点到达另一个点双的救援口。
如果某一个点双连通分量中有两个或以上的割顶,则它不需要救援口。因为它如果塌了其中的一个割点,能通过另一个割点到达其他的救援口。
最优性证明:以上方案中没有任何一个多余救援口,且能保证题意成立,且无法找到更优的方案。
第二问方案数:将每个点双根据以上三种情况进行统计,累乘即可。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define re register 6 #define rep(i, a, b) for (re int i = a; i <= b; ++i) 7 #define repd(i, a, b) for (re int i = a; i >= b; --i) 8 #define maxx(a, b) a = max(a, b); 9 #define minn(a, b) a = min(a, b); 10 #define LL long long 11 #define inf (1 << 30) 12 13 const int maxn = 1000 + 10; 14 15 struct Edge { 16 int u, v, pre; 17 }; 18 19 struct Graph { 20 Edge edges[maxn << 1]; 21 int n, m, G[maxn]; 22 void init() { 23 m = 0; 24 memset(G, 0, sizeof(G)); 25 } 26 void add(int u, int v) { 27 edges[++m] = (Edge){u, v, G[u]}; 28 G[u] = m; 29 } 30 int dfs_clock, iscut[maxn], low[maxn], pre[maxn]; 31 stack<pair<int, int> > S; 32 int bccno[maxn], bs, cnt[maxn]; 33 vector<int> bcc[maxn]; 34 void dfs(int u, int fa) { 35 low[u] = pre[u] = ++dfs_clock; 36 int child = 0; 37 for (register int i = G[u]; i; i = edges[i].pre) { 38 int v = edges[i].v; 39 if (!pre[v]) { 40 S.push(make_pair(u, v)); 41 child++; 42 dfs(v, u); 43 minn(low[u], low[v]); 44 if (low[v] >= pre[u]) { 45 iscut[u] = 1; 46 bcc[++bs].clear(); 47 for (;;) { 48 pair<int, int> top = S.top(); S.pop(); 49 if (bccno[top.first] != bs) bccno[top.first] = bs, bcc[bs].push_back(top.first); 50 if (bccno[top.second] != bs) bccno[top.second] = bs, bcc[bs].push_back(top.second); 51 if (top.first == u && top.second == v) break; 52 } 53 } 54 } 55 else if (v != fa) { 56 //S.push(make_pair(u, v)); 57 minn(low[u], pre[v]); 58 } 59 } 60 if (u == fa && child == 1) iscut[u] = 0; 61 } 62 pair<int, LL> tarjan() { 63 memset(pre, 0, sizeof(pre)); 64 memset(low, 0, sizeof(low)); 65 memset(iscut, 0, sizeof(iscut)); 66 memset(bccno, 0, sizeof(bccno)); 67 bs = 0; 68 rep(i, 1, n) 69 if (!pre[i]) 70 dfs(i, i); 71 memset(cnt, 0, sizeof(cnt)); 72 rep(i, 1, bs) 73 for (register int x = 0; x < bcc[i].size(); x++) 74 cnt[i] += iscut[bcc[i][x]] ? 1 : 0; 75 LL ans = 1; int sum = 0; 76 rep(i, 1, bs) { 77 int tot = bcc[i].size(); 78 if (!cnt[i]) sum += 2, ans *= (tot-1)*tot/2; 79 else if (cnt[i] == 1) 80 sum++, ans *= tot-1; 81 } 82 return make_pair(sum, ans); 83 } 84 } G; 85 86 int n, T = 0; 87 88 int main() { 89 while (~scanf("%d", &n)) { 90 T++; 91 int N = 0; 92 if (!n) break; 93 G.init(); 94 rep(i, 1, n) { 95 int u, v; 96 scanf("%d%d", &u, &v); 97 G.add(u, v); 98 G.add(v, u); 99 N = max(N, max(u, v)); 100 } 101 G.n = N; 102 pair<int, LL> ans = G.tarjan(); 103 printf("Case %d: %d %lld\n", T, ans.first, ans.second); 104 } 105 return 0; 106 }