如果邊數為奇數,一定無解。
如果邊數為偶數,一定有解。考慮證明:
我們可以先隨便定向,然後給每個點 \(i\) 一個值 \(a_i\in\{0,1\}\),表示出邊條數奇偶性。
然後隨便考慮圖的一顆生成樹。
注意到一條邊 \((u,v)\) 翻轉定向會讓 \(a_u\gets 1-a_u,a_v\gets 1-a_v\)。
這等於把 \(1\) 從邊的一段移動到另一端,碰上了就消掉。
有很多這種套路的,比如
- 這道。
- abc155_f Perils in Parallel
- CF1994F Stardew Valley
透過貪心滿足孩子節點,一定可以構造出一種邊定向的方案。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5, M = 2e5 + 5;
int m, n;
int deg[N];
bool ch[M], vis[N];
vector<pair<int, int>> e[N];
void dfs(int x, int fa)
{
vis[x] = 1;
for(auto [i, id] : e[x])
{
if(vis[i]) continue;
dfs(i, x);
if(deg[i])
{
deg[x] ^= 1;
ch[id] = 0;
}
}
}
int u[M], v[M];
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= m; i ++)
{
int x, y; cin >> x >> y;
ch[i] = 1;
u[i] = x, v[i] = y;
e[x].push_back({y, i});
e[y].push_back({x, i});
deg[x] ^= 1;
}
dfs(1, 0);
if(deg[1]) return cout << -1, 0;
for(int i = 1; i <= m; i ++)
if(ch[i]) cout << u[i] << " " << v[i] << "\n";
else cout << v[i] << " " << u[i] << "\n";
return 0;
}
構造生成樹可以用並查集,也可以直接 dfs 搜。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5, M = 2e5 + 5;
int m, n;
int fa[N];
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
int deg[N];
bool ch[M];
vector<pair<int, int>> e[N];
void dfs(int x, int fa)
{
for(auto [i, id] : e[x])
{
if(i == fa) continue;
dfs(i, x);
if(deg[i])
{
deg[x] ^= 1;
ch[id] = 0;
}
}
}
int u[M], v[M];
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i ++) fa[i] = i;
for(int i = 1; i <= m; i ++)
{
int x, y; cin >> x >> y;
u[i] = x, v[i] = y;
ch[i] = 1;
deg[x] ^= 1;
if(find(x) == find(y)) continue;
e[x].push_back({y, i});
e[y].push_back({x, i});
fa[find(x)] = find(y);
}
dfs(1, 0);
if(deg[1]) return cout << -1, 0;
for(int i = 1; i <= m; i ++)
if(ch[i]) cout << u[i] << " " << v[i] << "\n";
else cout << v[i] << " " << u[i] << "\n";
return 0;
}