并查集板子题。

对于并查集而言,很难进行删边操作,因此考虑将删边反过来,改为符合题意就加边。

由题意,同在 中的点不能相连,所以可以在并查集的合并过程中手动保持让 中的点作为并查集的根(同一个集合中最多有一个 中的点)。然后连边时,若两边的点(根)均为 中的点,则不连接,计入答案;否则连边,让 中的点(若存在)为根。

代码

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n, m;
    cin >> n >> m;
    vector<int> u(n + 7), v(n + 7), vis(n + 7), fa(n + 7);
    for (int i = 1; i <= n; i++)
        fa[i] = i;
    for (int i = 1; i < n; i++)
        cin >> u[i] >> v[i];

    for (int i = 1; i <= m; i++)
    {
        int k;
        cin >> k;
        vis[k] = 1;
    }
    function<int(int)> find = [&](int x) -> int
    {
        if (fa[x] == x)
            return x;
        return fa[x] = find(fa[x]);
    };
    using Pr = pair<int, int>;
    int ans = 0;
    vector<Pr> ans_;
    for (int i = 1; i < n; i++)
    {
        int ru = find(u[i]), rv = find(v[i]);
        if (ru == rv)
            continue;
        if (vis[ru] && vis[rv])
        {
            ans++;
            ans_.push_back(Pr(u[i], v[i]));
            continue;
        }
        if (vis[ru])
            fa[rv] = ru;
        else
            fa[ru] = rv;
    }
    cout << ans << '\n';
    for (auto [x, y] : ans_)
    {
        cout << x << ' ' << y << '\n';
    }
    return 0;
}