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 }