Luogu P2860 [USACO06JAN]Redundant Paths G

题目描述

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

简要题意:给定一个 $n$ 个点 $m$ 条边的无向连通图,求最少加多少条双向边使得该无向图可以变成边双连通图

$n\le 5000,m\le 10000$

Solution

边双缩点之后得到一棵树,将这棵树变成边双只需要将度数为 $1$ 的点都连起来

答案为 $(ans + 1) / 2$ ,$ans$ 是叶子节点的个数

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
#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 5010
#define maxm 10010
using namespace std;

int n, m, g[maxn][maxn];

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

int dfn[maxn], low[maxn], c2;
void tarjan(int u, int fa) {
dfn[u] = low[u] = ++c2;
for (int i = head[u]; ~i; i = e[i].next) {
int v = e[i].to, &f1 = e[i].f, &f2 = e[i ^ 1].f; if (v == fa) continue;
if (!dfn[v]) {
tarjan(v, u); low[u] = min(low[u], low[v]);
if (low[v] > dfn[u]) f1 = f2 = 1;
}
else low[u] = min(low[u], dfn[v]);
}
}

int fa[maxn];
void init_fa() {
for (int i = 1; i <= n; ++i) fa[i] = i;
}

int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

int bl[maxn], du[maxn];

int main() { memset(head, -1, sizeof head);
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
int x, y; cin >> x >> y;
add_edge(x, y); add_edge(y, x);
++g[x][y]; ++g[y][x];
} tarjan(1, 0); init_fa();
for (int i = 0; i < c1; i += 2) {
int u = e[i].fr, v = e[i].to, &f = e[i].f;
int fu = find(u), fv = find(v); if (g[u][v] > 1) f = 0;
if (fu == fv || f) continue; fa[fu] = fv;
} int dcc = 0;
for (int i = 1; i <= n; ++i) if (find(i) == i) bl[i] = ++dcc;
for (int i = 0; i < c1; i += 2) {
int u = e[i].fr, v = e[i].to, f = e[i].f;
int fu = find(u), fv = find(v); if (!f) continue;
++du[bl[fu]]; ++du[bl[fv]];
} int ans = 0;
for (int i = 1; i <= dcc; ++i) ans += du[i] == 1;
cout << (ans + 1) / 2 << endl;
return 0;
}