CF527E
分两部分考虑,第一部分是如何用最少的边使得每个点的度数、图的总边数都变成偶数;第二部分是如何进行具体的构造。
其中第一部分的构造又可以考虑先满足每个点的度数为偶数,再满足总边数为偶数。
满足每个点的度数为偶数这点比较好做:可以每次从度数为奇数的点中任选两个,在它们之间连一条边。
由于于度数为奇数的点必定有偶数个(根据度数的定义可得),故而可以这样做。
注意到此时可能会出现总边数为奇数的情况,只需要找到任意一个点,对它加个自环。
第二部分可以对着 $\text{dfs}$ 树进行构造。具体的,对于非树边,我们可以随意定向;在确定非树边方向之后,树边就有且仅有一个构造方案了——自底向上,根据一个点的奇偶性确定它到父亲的边是哪个方向。
由于总边数为偶数,可以证明这样的构造方法必定是可行的。
时间复杂度 $O(n+m)$。
#include <bits/stdc++.h>
#define FL(i, a, b) for(int i = (a); i <= (b); i++)
#define FR(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
const int N = 4e5 + 10;
int n, m, lst, tot, d[N], dfn[N], in[N], vis[N];
pair<int, int> g[N];
vector<pair<int, int> > e[N], ans;
void dfs(int u, int ed, int fa){
dfn[u] = ++tot;
for(auto &x: e[u]) if(x.second != (ed ^ 1)){
if(!dfn[x.first]) dfs(x.first, x.second, u);
else if(!vis[x.second ^ 1])
ans.emplace_back(make_pair(u, x.first)), d[u]++, vis[x.second] = 1;
}
if(d[u] & 1) ans.emplace_back(make_pair(u, fa));
else if(fa) ans.emplace_back(make_pair(fa, u)), d[fa]++;
}
int main(){
scanf("%d%d", &n, &m);
FL(i, 1, m){
scanf("%d%d", &g[i].first, &g[i].second);
d[g[i].first]++, d[g[i].second]++;
}
FL(i, 1, n){
if(d[i] & 1){
if(!lst) lst = i;
else g[++m] = make_pair(lst, i), lst = 0;
}
d[i] = 0;
}
if(m & 1) g[++m] = make_pair(1, 1);
FL(i, 1, m){
e[g[i].first].emplace_back(make_pair(g[i].second, i << 1));
e[g[i].second].emplace_back(make_pair(g[i].first, i << 1 | 1));
}
FL(i, 1, n) if(!dfn[i]) dfs(i, 0, 0);
printf("%d\n", ans.size());
for(auto &x: ans) printf("%d %d\n", x.first, x.second);
return 0;
}