agc014_e.Blue and Red Tree 题解

题目链接:https://atcoder.jp/contests/agc014/tasks/agc014_e

思路

将同时存在于两棵树中的边称为公共边。

1. 公共边可以直接缩点

对于公共边 \(u-v\),添加对应红边时,只能删除蓝边 \(u-v\) 本身,因为原蓝树中 \(u\) 到 \(v\) 的唯一蓝色路径就是这条边。

这次操作可以推迟到最后:保留这条蓝边不会妨碍其他操作。因此,原问题等价于将两棵树中的 \(u,v\) 同时缩成一个点后得到的问题。

另一方面,任何合法方案的最后一次操作,只剩下一条蓝边,添加的红边必须与它相同。因此,存在合法方案时一定存在公共边。

于是可以不断执行:

  • 找到任意公共边,将它的两个端点缩成一个点。
  • 最终只剩一个点,输出 YES。
  • 还有多个点但没有公共边,输出 NO。

2. 高效维护缩点

使用:

  • 并查集维护每个点所在的分量。
  • vector<int> g[maxn] 保存各分量包含的原始邻接记录。
  • set<pair<int, int>> s 保存当前分量之间的边,每对端点只保留一次。
  • 队列保存已经发现的公共边。

合并分量 \(x,y\) 时,将邻接表较短的 \(y\) 合并到 \(x\)。

对于原来连接 \(y,v\) 的边,将它改为 \(x,v\)。如果 \(x,v\) 已经存在,就发现了一条新的公共边。因为两棵树缩点后仍然分别是树,不可能出现同色重边。

邻接表可能包含重复或已经成为内部边的记录,利用并查集和 set 跳过即可。

复杂度

每条邻接记录每次移动后,所在邻接表的长度至少翻倍,所以最多移动 \(O(\log N)\) 次。

时间复杂度为 \(O(N\log^2 N)\),空间复杂度为 \(O(N)\)。

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n, cnt, fa[maxn];
vector<int> g[maxn];
set<pair<int, int>> s;
queue<pair<int, int>> q;

int find(int x) {
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}

bool check() {
    cnt = n;
    while (!q.empty()) {
        int x = find(q.front().first), y = find(q.front().second);
        q.pop();
        if (x == y) continue;
        if (g[x].size() < g[y].size()) swap(x, y);

        s.erase({min(x, y), max(x, y)});
        fa[y] = x;

        for (int v : g[y]) {
            v = find(v);
            if (v == x) continue;
            if (!s.erase({min(y, v), max(y, v)})) continue;
            if (!s.insert({min(x, v), max(x, v)}).second)
                q.push({x, v});
        }

        g[x].insert(g[x].end(), g[y].begin(), g[y].end());
        vector<int>().swap(g[y]);
        cnt--;
    }
    return cnt == 1;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) fa[i] = i;

    for (int i = 1, u, v; i <= 2 * n - 2; i++) {
        scanf("%d%d", &u, &v);
        g[u].push_back(v);
        g[v].push_back(u);
        if (u > v) swap(u, v);
        if (!s.insert({u, v}).second)
            q.push({u, v});
    }

    puts(check() ? "YES" : "NO");
    return 0;
}
posted @ 2026-10-09 19:18  quanjun  阅读(13)  评论(0)    收藏  举报