VK2012的练习题 http://codeforces.com/problemset/problem/209/C
题目模型比较简单,在连通情况不明的无向图中加入边,使得该图存在一条欧拉路径,从点1开始回到点1。
相信很多有经验的Coder已经想到了大致的算法,首先用并查集求得各个连通集,然后对每个连通集合“伸出”两条边,并“消去”内部的奇度点,注意一些Trick情况就能得到解。
我拿出这道题目是因为对一个优化产生了怀疑,传说中在Merge两个集合的时候加入数量判断能避免算法O的退化,然而事实是这样的:
在优化之后,反而大大增加了计算时间。我的估计是本身在有大量查询的情况下,该算法是很难退化的,而加入的数量判断反而增加了时间。不得不赞叹并查集算法的优美和高效。
#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; }