QOJ.ac

QOJ

Type: Editorial

Status: Open

Posted by: jiangly

Posted at: 2025-12-13 00:37:58

Last updated: 2025-12-13 00:38:01

Back to Problem

题解

题意

给定一棵 $N$ ($1\le N\le 5\cdot10^5$) 个点的树,每个点属于一个集合。你可以进行任意次合并两个集合的操作,使得不存在一种方案将所有集合划分为两个非空的连通块。最小化操作次数。

题解

将所有相同颜色的点对路径上的点合并,形成一棵树,问题转化为用最少的路径覆盖所有的边,因此答案为 $\lceil l/2\rceil$ ($l$ 是度为 $1$ 的点数)。

时间复杂度:$O(n\log n)$ 或 $O(n\alpha(n))$。

代码

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int n, k;
    std::cin >> n >> k;
    std::vector<int> x(n - 1), y(n - 1);
    std::vector<std::vector<int>> e(n);
    for (int i = 0; i < n - 1; ++i) {
        std::cin >> x[i] >> y[i];
        --x[i];
        --y[i];
        e[x[i]].push_back(y[i]);
        e[y[i]].push_back(x[i]);
    }
    std::vector<int> dep(n), parent(n);
    std::vector<int> fa(n);
    std::iota(fa.begin(), fa.end(), 0);
    auto find = [&](int x) {
        while (fa[x] != x)
            x = fa[x] = fa[fa[x]];
        return x;
    };
    std::function<void(int)> dfs = [&](int u) {
        for (auto v : e[u]) {
            if (v == parent[u])
                continue;
            dep[v] = dep[u] + 1;
            parent[v] = u;
            dfs(v);
        }
    };
    parent[0] = -1;
    dfs(0);
    std::vector<int> last(k, -1);
    auto cover = [&](int u, int v) {
        u = find(u);
        v = find(v);
        while (u != v) {
            if (dep[u] < dep[v])
                std::swap(u, v);
            fa[u] = parent[u];
            u = find(u);
        }
    };
    for (int i = 0; i < n; ++i) {
        int x;
        std::cin >> x;
        --x;
        if (last[x] != -1)
            cover(last[x], i);
        last[x] = i;
    }
    std::vector<int> deg(n);
    for (int i = 0; i < n - 1; ++i) {
        x[i] = find(x[i]);
        y[i] = find(y[i]);
        if (x[i] != y[i]) {
            ++deg[x[i]];
            ++deg[y[i]];
        }
    }
    std::cout << (std::count(deg.begin(), deg.end(), 1) + 1) / 2 << "\n";
    return 0;
}

Comments

No comments yet.