cychester

BZOJ2730 [HNOI2012]矿场搭建 - Tarjan割点

Solution

输入中没有出现过的矿场点是不用考虑的, 所以不用考虑只有 一个点 的点双联通分量。

要使某个挖矿点倒塌, 相当于割去这个点, 所以我们求一遍割点和点双联通分量。

之后的点双联通分量构成一棵树。 树上的节点有两种情况

 

1: 仅有一条边(仅有 一个割点 在内部)—— 相当与叶子节点, 把它与父亲节点相连的割点割去后,必须在里面设一个逃生出口

2: 大于一条边(有大于一个割点在内部 )——割去其中一个割点时, 还可以通过另一个割点到达逃生出口, 所以不用设置。

 

所以我们要求的就是在所有 仅含一个割点的 点双联通分量 内设置 一个 逃生出口, 并根据 乘法原理 计算方案数。

特别的: 当整个图是一个 点双连通图时, 设置任意 两个 逃生出口即可。

 

Code

 1 #include<cstdio>
 2 #include<vector>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define rd read()
 6 #define R register
 7 #define ll long long
 8 using namespace std;
 9 
10 const int N = 1e3;
11 
12 int head[N], tot;
13 int dfn[N], low[N], col_num;//col_num为点双联通分量个数
14 int n, m, mark[N], cut[N], rt, maxn, cut_num[N];//cut_num为点双联通分量内的割点数
15 int st[N], tp, cnt;
16 ll ans1, ans2;
17 
18 vector<int> q[N];
19 struct edge {
20     int nxt, to, fr;
21 }e[N << 3];
22 
23 int read() {
24     int X = 0, p = 1; char c = getchar();
25     for(; c > '9' || c < '0'; c = getchar()) if(c == '-') p = -1;
26     for(; c >= '0' && c <= '9'; c = getchar()) X = X * 10 + c - '0';
27     return X * p;
28 }
29 
30 void add(int u, int v) {
31     e[++tot].to = v;
32     e[tot].nxt = head[u];
33     e[tot].fr = u;
34     head[u] = tot;
35 }
36 
37 void tarjan(int u) {
38     dfn[u] = low[u] = ++cnt;
39     st[++tp] = u;
40     int flag = 0;
41     for(R int i = head[u]; i; i = e[i].nxt) {
42         R int nt = e[i].to;
43         if(!dfn[nt]) {
44             tarjan(nt);
45             low[u] = min(low[u], low[nt]);
46             if(low[nt] >= dfn[u]) {
47                 col_num++;
48                 flag ++;
49                 if(flag > 1 || u != rt)
50                     cut[u] = 1;
51                 for(; tp;) {
52                     int z = st[tp--];
53                     q[col_num].push_back(z);
54                     if(z == nt) break;
55                 }
56                 q[col_num].push_back(u);
57             }
58         } else low[u] = min(low[u], dfn[nt]);
59     }
60 }
61 
62 void init() {
63     for(int i = 1; i <= col_num; ++i)
64         q[i].clear();
65     ans1 = maxn = col_num = cnt = tot = 0;
66     ans2 = 1;
67     memset(dfn, 0, sizeof(dfn));
68     memset(mark, 0, sizeof(mark));
69     memset(cut, 0, sizeof(cut));
70     memset(low, 0, sizeof(low));
71     memset(head, 0, sizeof(head));
72     memset(cut_num, 0, sizeof(cut_num));
73 }
74 
75 int main()
76 {
77     for(int T = 1; ; T++) {
78         n = rd;
79         if(!n) return 0;
80         init();
81         for(int i = 1; i <= n; ++i) {
82             int u = rd, v = rd;
83             add(u, v); add(v, u);
84             mark[u] = mark[v] = 1;
85             maxn = max(maxn, u);
86             maxn = max(maxn, v);
87         }
88         for(int i = 1; i <= maxn; ++i)
89             if(!dfn[i] && mark[i]) tarjan(rt = i);
90         for(int i = 1; i <= col_num; ++i)
91             for(int j = 0, len = q[i].size(); j < len; ++j) {
92                 if(cut[q[i][j]]) cut_num[i]++;
93             }
94         for(int i = 1; i <= col_num; ++i)
95                 if(cut_num[i] == 1) ans1++, ans2 = ans2 * (int)(q[i].size() - 1);
96         printf("Case %d: %lld %lld\n", T, ans1 ? ans1 : 2, ans1 ? ans2 : (int)(q[1].size() - 1) * q[1].size()/ 2);
97     }
98 }
View Code

 

posted on 2018-09-12 14:55  cychester  阅读(194)  评论(0编辑  收藏  举报

导航