「解题报告」AGC025E Walking on a Tree
好神奇的题。
首先观察样例发现答案都能卡到答案的上界,那么我们盲猜有一种构造方法能够使得答案达到上界。
考虑每次找到一个叶子。那么经过叶子到父亲的这条边的路径一定有一个端点为 \(u\)。考虑所有的这样的路径 \((u, v)\)。
假如这样的路径只有一条,那么我们发现这条路径选啥其实都无所谓,所以可以把这条路径缩短一下,然后把这个叶子删去。
如果这样的路径有多于一条,那么我们考虑先选出两条边钦定两条边选一正一反。那么,我们发现,对于两条路径的公共部分肯定就都是一正一反了,而公共部分之外的部分形成了一条新的路径。那么我们就可以将两条路径删去,然后加入这一条新的路径。然后剩下的路径选啥不重要,于是直接缩短一下,然后删除这个叶子。
容易发现,这样一直删到只剩一个点后,所有的边的覆盖次数全部达到了上界。
Alternate Solution: 考虑直接将每条路径建成一张无向图。假如存在欧拉回路,那么直接按照欧拉回路定向,那么每条边都会恰好覆盖偶数次,且正反次数相等。如果不存在欧拉回路,那么我们可以适当的添加一些不相交的边使得存在欧拉回路,由于每个边最多被新增加的边覆盖一次,所以删去新增加的边后一定仍然同时存在正反的两条边(如果这条边原来被覆盖了大于等于两次)。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5000005;
int n, m;
vector<int> e[MAXN];
int deg[MAXN];
int u[MAXN], v[MAXN], d[MAXN], f[MAXN];
int fa[MAXN], dep[MAXN];
void dfs(int u, int pre) {
fa[u] = pre, dep[u] = dep[pre] + 1;
for (int v : e[u]) if (v != pre) {
dfs(v, u);
}
}
int c[MAXN];
void update(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
while (dep[u] > dep[v]) c[u]++, u = fa[u];
while (u != v) c[u]++, c[v]++, u = fa[u], v = fa[v];
}
struct Edge {
int v, i, d;
bool operator<(const Edge &b) const {
return v == b.v ? i < b.i : v < b.v;
}
};
set<Edge> s[MAXN];
int newPath(int u, int v, int d) {
if (u == v) return 0;
if (d) swap(u, v);
::u[++m] = u, ::v[m] = v;
s[u].insert(Edge{ v, m, 0 });
s[v].insert(Edge{ u, m, 1 });
return m;
}
void deletePath(int i) {
s[u[i]].erase(Edge{ v[i], i, 0 });
s[v[i]].erase(Edge{ u[i], i, 1 });
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
e[u].push_back(v), e[v].push_back(u);
deg[u]++, deg[v]++;
}
dfs(1, 0);
for (int i = 1; i <= m; i++) {
scanf("%d%d", &u[i], &v[i]);
update(u[i], v[i]);
s[u[i]].insert(Edge{v[i], i, 0});
s[v[i]].insert(Edge{u[i], i, 1});
}
int p = m;
queue<int> q;
for (int i = 2; i <= n; i++) if (deg[i] == 1) {
q.push(i);
}
while (!q.empty()) {
int u = q.front(); q.pop();
if (u == 1) continue;
if (s[u].size() == 1) {
auto e = *s[u].begin();
int i = e.i;
deletePath(i);
f[i] = newPath(fa[u], e.v, e.d);
} else if (s[u].size() > 1) {
auto e1 = *s[u].begin();
deletePath(e1.i);
auto e2 = *s[u].begin();
deletePath(e2.i);
int i = newPath(e1.v, e2.v, 0);
f[e1.i] = i, d[e1.i] = e1.d ^ 1;
f[e2.i] = i, d[e2.i] = e2.d;
while (s[u].size()) {
auto e = *s[u].begin();
int i = e.i;
deletePath(i);
f[i] = newPath(fa[u], e.v, e.d);
}
}
deg[fa[u]]--;
if (deg[fa[u]] == 1) {
q.push(fa[u]);
}
}
for (int i = m; i >= 1; i--) {
d[i] ^= d[f[i]];
}
int ans = 0;
for (int i = 2; i <= n; i++) {
ans += min(2, c[i]);
}
printf("%d\n", ans);
for (int i = 1; i <= p; i++) {
if (d[i]) swap(u[i], v[i]);
printf("%d %d\n", u[i], v[i]);
}
return 0;
}