题目描述
小sun最近为了应付考试,正在复习图论,他现在学到了图的遍历,觉得太简单了,于是他想到了一个更加复杂的问题:

无向图有n个点,从点1开始遍历,但是规定:按照每次“走两步”的方式来遍历整个图。可以发现按照每次走两步的方法,不一定能够遍历整个图,所以现在小sun想问你,最少加几条边,可以完整的遍历整个图。

输入描述:
第一行两个整数n,m代表图的点数和边数。

接下来m行,每行两个整数u,v代表u,v有边相连(无向边)
输出描述:
输出一行,代表最少要添加的边数。
示例1
输入
复制

5 4
1 2
2 3
3 4
4 5
输出
复制

1
示例2
输入
复制

5 5
1 2
2 3
3 4
4 5
1 5
输出
复制

0
备注:
数据范围:

3\leq n\leq1e5, \ 2\leq m\leq 1e53≤n≤1e5, 2≤m≤1e5
1\leq u,v\leq n1≤u,v≤n
图中点的编号从1到n。

走两步的意思:
比如现在有两条边:(1,2),(2,3),从1开始走,只能走到1或者3。
(1->2->3),(1->2->1)


很明显就是判断奇环,如果有奇环,那么从奇环的任何一个点开始,都能到达奇环的所有点,所以我们对每个联通块来考虑即可。

如果某个联通块有奇环,那么我们只需要把所有的联通块连到一起即可,所以答案就是联通块个数减一。
如果不存在奇环,那么我们需要把奇数个联通块连到一起,再连向其他的偶联通块即可。所以答案就是联通块的个数。


怎么判断奇环:

求联通块个数很简单,dfs一遍即可。

找奇环,我们可以对联通块染色,染互相间隔的0,1,所以如果是偶数环,那么相邻的不会有相同的数字,否则为奇环。(二分图判定定理)。


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e5+10;
int n,m,cnt,flag,col[N];
int head[N],nex[N<<1],to[N<<1],tot;
inline void add(int a,int b){
	to[++tot]=b; nex[tot]=head[a]; head[a]=tot;
}
void dfs(int x,int fa){
	for(int i=head[x];i;i=nex[i]){
		if(to[i]==fa||col[to[i]]!=-1)	continue;
		col[to[i]]=col[x]^1;	dfs(to[i],x);
	}
}
signed main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++){
		int a,b;	scanf("%d %d",&a,&b);	add(a,b);	add(b,a);
	}
	memset(col,-1,sizeof col);
	for(int i=1;i<=n;i++)	if(col[i]==-1)	cnt++,col[i]=1,dfs(i,0);
	for(int i=1;i<=n;i++){
		for(int j=head[i];j;j=nex[j]){
			if(col[to[j]]==col[i])	return printf("%d\n",cnt-1),0;
		}
	}
	printf("%d\n",cnt);
	return 0;
}