题意
传送门 Codeforces 723E One-Way Reform
题解
依次处理每一个连通分量。无向图中度数为奇数的点,在转化为有向图之后,不可能满足出度等于入度的条件;这样的点有偶数个,因为总度数为偶数。
将 i , i + 1 , ⋯ i + k i,i+1,\cdots i+k i,i+1,⋯i+k 两两连边,即 ( i , i + 1 ) , ( i + 2 , i + 3 ) ⋯ (i,i+1),(i+2,i+3)\cdots (i,i+1),(i+2,i+3)⋯,此时满足联通分量中所有点的度数为偶数,图中存在欧拉回路。沿着欧拉回路赋予边方向,则原图中度数为偶数的点满足条件,此时取到了答案可达的上界。总时间复杂度 O ( n + m ) O(n+m) O(n+m)。
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
typedef long long ll;
const int MAXN = 2E5 + 5;
struct edge
{
int to, rev;
bool add, used;
};
vector<edge> G[MAXN];
vector<int> cc, odd, path, ban;
vector<pair<int, int>> res;
int T, N, M, deg[MAXN], iter[MAXN];
bool used[MAXN];
void dfs(int u)
{
used[u] = 1;
cc.pb(u);
for (auto &e : G[u])
if (!used[e.to])
dfs(e.to);
}
void euler(int u, bool b)
{
for (int &i = iter[u]; i < (int)G[u].size();)
{
while (i < (int)G[u].size() && G[u][i].used)
++i;
if (i == (int)G[u].size())
break;
auto &e = G[u][i];
G[e.to][e.rev].used = 1;
++i;
euler(e.to, e.add);
}
path.pb(u);
ban.pb(b);
}
void add_edge(int u, int v, bool add)
{
G[u].pb({v, (int)G[v].size(), add, 0});
G[v].pb({u, (int)G[u].size() - 1, add, 0});
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> T;
while (T--)
{
cin >> N >> M;
res.clear();
for (int i = 0; i < N; ++i)
{
deg[i] = iter[i] = used[i] = 0;
G[i].clear();
}
for (int i = 0; i < M; ++i)
{
int u, v;
cin >> u >> v;
--u, --v;
add_edge(u, v, 0);
++deg[u], ++deg[v];
}
int num = N;
for (int i = 0; i < N; ++i)
{
if (used[i])
continue;
cc.clear(), odd.clear();
path.clear(), ban.clear();
dfs(i);
for (auto &u : cc)
if (deg[u] & 1)
odd.pb(u);
num -= (int)odd.size();
for (int j = 0; j < (int)odd.size(); j += 2)
add_edge(odd[j], odd[j + 1], 1);
euler(i, 0);
for (int j = 0; j + 1 < (int)path.size(); ++j)
{
if (ban[j])
continue;
res.pb({path[j + 1], path[j]});
}
}
cout << num << '\n';
for (auto &e : res)
cout << e.first + 1 << ' ' << e.second + 1 << '\n';
}
return 0;
}