題意:
給定一張 \(n\) 個點 \(m\) 條邊無向連通圖,以及 \(q\) 個點對 \((a,b)\),出事每條邊權值為 \(0\)。對於每個點對我們需要找一條從一個點到另一個點的簡單路徑,將所有邊的權值加一。要求構造一種方案使得每條邊權值都是偶數。如果不行,輸出最少還要幾個點對才能滿足要求。
\(n,m \le 10^5\)。
思路:
這個條件不難聯想到尤拉回路,我們嘗試構造一個圖 \(G'\) 使得這 \(q\) 個點對是這張圖的所有邊。
如果這張圖存在尤拉回路,則我們只用在迴路最後一條邊反向走一遍便能滿足要求。回到原圖,隨便一棵生成樹上走兩點路徑即可。
如果不存在尤拉回路,說明一定有奇點,這就會導致有的邊邊權是奇數。具體而言,我們記錄每個點所有相鄰邊的邊權和的奇偶性。顯然一個點對只會改變兩個端點,所以如果不存在尤拉回路,則必然不成立。需要再加上奇點個數除以二個。
然後就沒了。
點選檢視程式碼
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 3e5 + 5;
int n, m, q;
vector<int> e[N], E[N];
int cnt[N] = {0};
int a[N] = {0}, b[N] = {0};
int p[N] = {0};
int fnd(int x) {
if (p[x] == x)
return x;
return p[x] = fnd(p[x]);
}
void unn(int x, int y) {
p[fnd(x)] = fnd(y);
}
int pr[N] = {0}, dth[N] = {0};
void srh(int x, int Pr, int d) {
dth[x] = d, pr[x] = Pr;
for (auto i: E[x])
if (i != Pr)
srh(i, x, d + 1);
}
int fnd_lca(int x, int y) {
if (x == y)
return x;
if (dth[x] < dth[y])
swap(x, y);
return fnd_lca(pr[x], y);
}
void fnd_prx(int x, int anc) {
if (x == anc)
return;
printf("%d ", x);
fnd_prx(pr[x], anc);
}
void fnd_suf(int x, int anc) {
if (x == anc)
return;
fnd_suf(pr[x], anc);
printf("%d ", x);
}
void fnd_pth(int x, int y) {
int lca = fnd_lca(x, y);
printf("%d\n", dth[x] + dth[y] - 2 * dth[lca] + 1);
fnd_prx(x, lca);
printf("%d ", lca);
fnd_suf(y, lca);
printf("\n");
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1, u, v; i <= m; i++) {
scanf("%d%d", &u, &v);
e[u].push_back(v);
e[v].push_back(u);
}
scanf("%d", &q);
for (int i = 1; i <= q; i++) {
scanf("%d%d", &a[i], &b[i]);
cnt[a[i]]++, cnt[b[i]]++;
}
int odd = 0;
for (int i = 1; i <= n; i++)
odd += cnt[i] % 2;
if (odd > 0) {
printf("No\n%d\n", odd / 2);
return 0;
}
for (int i = 1; i <= n; i++)
p[i] = i;
for (int i = 1; i <= n; i++)
for (auto j: e[i])
if (fnd(i) != fnd(j)) {
unn(i, j);
E[i].push_back(j);
E[j].push_back(i);
}
srh(1, 1, 1);
printf("YES\n");
for (int i = 1; i <= q; i++)
fnd_pth(a[i], b[i]);
return 0;
}