CodeForces 209C

VK2012的练习题 http://codeforces.com/problemset/problem/209/C

题目模型比较简单,在连通情况不明的无向图中加入边,使得该图存在一条欧拉路径,从点1开始回到点1。

相信很多有经验的Coder已经想到了大致的算法,首先用并查集求得各个连通集,然后对每个连通集合“伸出”两条边,并“消去”内部的奇度点,注意一些Trick情况就能得到解。

我拿出这道题目是因为对一个优化产生了怀疑,传说中在Merge两个集合的时候加入数量判断能避免算法O的退化,然而事实是这样的:

CodeForces 209C_第1张图片

在优化之后,反而大大增加了计算时间。我的估计是本身在有大量查询的情况下,该算法是很难退化的,而加入的数量判断反而增加了时间。不得不赞叹并查集算法的优美和高效。

#include <algorithm>
#include <cstdio>
#include <string>
#include <queue>
#include <map>
using namespace std;

#define MAXLONG 0x7FFFFFFF
#define LL long long
#define MAXN 1011110

int fa[MAXN];
int de[MAXN];

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

void Union(int x, int y)
{
	x = FindFather(x), y = FindFather(y);
	if(x > y) swap(x, y);
	fa[y] = x;
}

map<int, int> cnt;

int main()
{
#if _MSC_VER == 1500
	freopen("in.txt", "r", stdin);
#endif
	int n, m;
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; ++ i)
		fa[i] = i;
	for(int i = 0; i < m; ++ i)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		de[x] ++, de[y] ++;
		Union(x, y);
	}
	cnt[1] = 0;
	for(int i = 1; i <= n; ++ i)	
	{
		fa[i] = FindFather(i);
		if(de[i] == 0)			// do not forget to exclude isolated points
			continue;
		if(de[i] & 1)
			cnt[fa[i]] += 1;
		else
			cnt[fa[i]] += 0;
	}
	map<int, int>::iterator it;
	int ans = 0;
	int sz = cnt.size();
	ans += sz == 1 ? 0 : sz;

	for(it = cnt.begin(); it != cnt.end(); ++ it)
	{
		int itemp;
		itemp = it->second;
		if(itemp >= 2 && sz > 1)
			itemp -= 2;
		ans += itemp / 2;
	}
	printf("%d\n", ans);
	
	return 0;
}

你可能感兴趣的:(CodeForces 209C)