Luogu P3225 [HNOI2012]矿场搭建

题目描述

https://www.luogu.com.cn/problem/P3225

Solution

注意到只有一个割点的点双需要建立救援出口,且救援出口一定不能建在割点上

另外需要注意到如果存在没有割点的点双,需要建两个救援出口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>
#define maxn 510
#define maxm 510
#define pb push_back
#define ll long long
using namespace std;

int n, m;

struct Edge {
int to, next;
} e[maxm * 2]; int c1, head[maxn];
inline void add_edge(int u, int v) {
e[c1].to = v; e[c1].next = head[u]; head[u] = c1++;
}

int dfn[maxn], low[maxn], c2, dc;
stack<int> S; vector<int> dcc[maxn]; bool use[maxn];
void tarjan(int u, int fa) {
dfn[u] = low[u] = ++c2; S.push(u); int du = 0;
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to; if (v == fa) continue;
if (!dfn[v]) { ++du;
tarjan(v, u); low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u]) {
int t; ++dc;
do {
t = S.top(); S.pop();
dcc[dc].pb(t);
} while (t != v);
dcc[dc].pb(u);
if (fa) use[u] = 1;
}
}
else low[u] = min(low[u], dfn[v]);
}
if (!fa && du > 1) use[u] = 1;
}

void init() {
memset(head, -1, sizeof head); c1 = c2 = dc = 0;
for (int i = 0; i < maxn; ++i)
dcc[i].clear(), low[i] = dfn[i] = use[i] = 0;
n = 0;
}

int icase;
void work() { init();
for (int i = 1; i <= m; ++i) {
int x, y; scanf("%d%d", &x, &y);
n = max(n, x); n = max(n, y);
add_edge(x, y); add_edge(y, x);
}
for (int i = 1; i <= n; ++i)
if (!dfn[i]) tarjan(i, 0);
int ans = 0; ll Ans = 1;
for (int i = 1; i <= dc; ++i) {
int s = 0; ll _s = dcc[i].size();
for (auto u : dcc[i]) s += use[u];
if (s == 1) Ans *= _s - 1, ++ans;
else if (!s) Ans *= _s * (_s - 1) / 2, ans += 2;
}
printf("Case %d: %d %lld\n", ++icase, ans, Ans);
}

int main() {
while (cin >> m && m) work();
return 0;
}