题目链接
这怕是
poj
p
o
j
除了
A+B Problem
A
+
B
P
r
o
b
l
e
m
最良心的题了
边双比点双好求多了 因为有个很棒的性质就是 low l o w 相同的点都在一个边双里,因为一个边双上面肯定是桥边,而桥边上面的点 dfs d f s 时间戳一定小于下面的点,所以下面的点就成了一个边双,缩完点后发现是一棵树,让树变成边双的最小代价就是 叶子节点数+12 叶 子 节 点 数 + 1 2 , 因为每加一条边最多连接两个叶子
update u p d a t e 上面这个东西只有在没重边的时候才有用
Codes
#include<cstring>
#include<cstdio>
#include<iostream>
#define mem(a, b) memset(a, b, sizeof(a))
using namespace std;
const int N = 1000 + 10, M = 2000 + 10;
int to[M], head[N], nxt[M], e;
int low[N], dfn[N], vis[N], de[N];
int n, m, cnt, ans;
void add(int x, int y, bool k = 1) {
to[++ e] = y; nxt[e] = head[x]; head[x] = e;
if(k) add(y, x, 0);
}
struct edge {
int x, y;
}E[M];
void dfs(int x, int fa) {
dfn[x] = low[x] = ++ cnt;
for(int i = head[x]; i; i = nxt[i]) {
if(!dfn[to[i]]) {
dfs(to[i], x);
low[x] = min(low[x], low[to[i]]);
}
if(to[i] != fa)
low[x] = min(low[x], dfn[to[i]]);
}
vis[low[x]] = true;
}
int main() {
// freopen("3352.in", "r", stdin);
// freopen("3352.out", "w", stdout);
mem(head, 0);
mem(dfn, 0);
mem(vis, 0);
mem(de, 0);
e = cnt = ans = 0;
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++ i) {
scanf("%d%d", &E[i].x, &E[i].y);
add(E[i].x, E[i].y);
}
for(int i = 1; i <= n; ++ i)
if(!dfn[i])
dfs(i, 0);
for(int i = 1; i <= m; ++ i) {
if(low[E[i].x] == low[E[i].y])
continue;
++ de[low[E[i].x]], ++ de[low[E[i].y]];
}
for(int i = 1; i <= n; ++ i)
if(vis[i] && de[i] == 1)
++ ans;
printf("%d\n", (ans + 1) >> 1);
return 0;
}