这题想到一半了,最后缩点以后的统计答案挂了= =。。。
题意就不说了,问你加入多少条边能使得所有边都是一个边双。
n有点小了,怀疑有n^2水法。
求出边双以后缩点比较显然,然后我就有点蒙逼了,不知道怎么统计答案。。
想了dp和二分等奇怪姿势,然而好像都没用。
后来看了一波题解才发现是结论= =一棵树,要让他任意两点之间有超过1条路径相连,答案是(叶子节点个数+1)/2。
为啥呢?因为,一棵树,我们一开始不加边,他们之间的路径肯定是向上走然后相交,那么第二条路径,我们要加的边尽量少,则重复的边尽量少,所以我们在不同子树之间的叶子节点之间两两配对就好了,当然如果只有一个叶子节点就两个都连过去。。
注意最后统计叶子节点的时候,只有是桥的边才是树上的边。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
int n, m;
const int N = 1e5 + 5;
int read()
{
int x = 0, f = 1; char ch = getchar();
while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x*f;
}
int dfn[N], low[N],dep[N];
bool vis[N],isc[N],isb[N];
int head[N], go[N], nxt[N];
int tot=1, cnt, tim;
inline void add(int x, int y)
{
go[++tot] = y;
nxt[tot] = head[x];
head[x] = tot;
}
inline void dfs(int x, int pre)
{
dfn[x]=low[x]=++tim;
for (int i = head[x]; i; i = nxt[i])
{
int v = go[i];
if ((i^1)!=pre)
if (!dfn[v])
dfs(v, i),
low[x] = min(low[x], low[v]);
else low[x] = min(low[x], dfn[v]);
}
if (pre&&low[x] == dfn[x])
isb[pre] = isb[pre ^ 1] = 1;
}
int bel[N];
inline void rebuild(int x, int num)
{
bel[x] = num;
for (int i = head[x]; i; i = nxt[i])
{
int v = go[i];
if (!bel[v] && !isb[i])rebuild(v, num);
}
}
int main()
{
n = read(), m = read();
fo(i, 1, m)
{
int x=read(), y=read();
add(x, y);
add(y, x);
}
int blocksum = 0;
fo(i, 1, n)if (!dfn[i])dfs(1,0);
fo(i, 1, n)if (!bel[i])rebuild(i, ++blocksum);
for (int i = 2; i <= tot; i += 2)if (isb[i])dep[bel[go[i]]]++, dep[bel[go[i ^ 1]]]++;
int ans = 0;
fo(i, 1, blocksum)if (dep[i] == 1)ans++;
printf("%d\n", (ans + 1) >> 1);
}