[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 }

 

posted @ 2019-02-02 16:42  AC-Evil  阅读(211)  评论(0编辑  收藏  举报