「解题报告」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;
}
posted @ 2023-05-12 11:40  APJifengc  阅读(53)  评论(0编辑  收藏  举报