Loading

UOJ#883. 【UR #27】景点观光

不难发现子树中没有任何景点的结点都是无用的,可以把这些点删去,称此时的新树为 \(\mathcal T\)

那么 \(k = 0\) 时的步数恒定,为 \(2E_\mathcal T\)

我们要做的就是求出 \(k\) 的最大值 \(k_m\),每次询问的答案即 \(2E_\mathcal T - \min\{k_m, k\}\)

如果我们一直跳两条边,那么能跳到的结点的深度的奇偶性是确定的(只能全奇或全偶),而每跳一次单边,能跳到的结点的深度的奇偶性就会改变一次。

dfs 的过程中判断奇偶性改变的次数即可。

时间复杂度 \(\mathcal O(\sum n + \sum q)\)

代码

#include <bits/stdc++.h>

using namespace std;

void solve() {
    int n, m, q;
    cin >> n >> m >> q;

    vector<vector<int>> G(n + 1);
    vector<bool> vis(n + 1);

    for (int i = 1, u, v; i < n; i++) cin >> u >> v, G[u].emplace_back(v), G[v].emplace_back(u);
    for (int i = 1, x; i <= m; i++) cin >> x, vis[x] = 1;
    vis[1] = 1; 

    int cnt = 0, ban = 0;
    vector<int> t(n + 1);
    auto dfs = [&](auto &&self, int u, int fa) -> void {
        vector<int> c(4); c[1] = vis[u];
        for (int v : G[u]) if (v != fa) {
            self(self, v, u);
            if (t[v] >= 0) cnt += 2, c[t[v]]++;
        }
        if (!(c[0] | c[1] | c[2] | c[3])) {t[u] = -1; return;}
        if (c[3]) {t[u] = (c[3] & 1) ? 3 : 0; return;}
        t[u] = 0;
        if (c[1]) t[u] |= 2;
        if (c[2]) t[u] |= 1;
        if (t[u] == 3) ban++;
    };

    dfs(dfs, 1, -1);
    if (t[1] == 1) ban += 2;
    else if (t[1] == 3) ban++;
    
    int maxk = (cnt - ban) >> 1;

    for (int k; q--;) cin >> k, cout << cnt - min(k, maxk) << ' ';
    cout << '\n';
}

int main() {
    ios::sync_with_stdio(0); cin.tie(nullptr), cout.tie(nullptr);
    int t; cin >> t;
    while (t--) solve();
    return 0;
}
posted @ 2024-05-24 11:50  Chy12321  阅读(36)  评论(0编辑  收藏  举报