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;
}