2024ICPC武汉邀请赛E. Boomerang 题解
E - Boomerang(动态维护树的直径 + 二分)
分析
首先考虑对于当\(t_0 + x\)时刻的传播谣言的树形图,可以发现选择这棵树直径的中点开始进行辟谣是最优的,那么只需要二分一下时间,即在二分给定的时间内开始以\(k=i\)的速度辟谣,能否在二分的时间内辟谣成功(能否跑完这课谣言树)。
剩下的问题就是如何快速求出对于\(t_0 + x\)时刻谣言树的直径,显然的可以使用一个集合用来记录当前这个集合的两个直径端点,每次添加时用用新添加的点和原来端点距离进行比较,以此来更新直径和直径端点,两点间距离使用LCA来求即可。
代码实现
#include <bits/stdc++.h>
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
#define int long long
using Edge = int;
struct HLD {
int n, times = 0;
std::vector<int> siz, top, dep, fa, in, out, seq;
std::vector<std::vector<Edge>> adj;
HLD(const auto &adj, int root = 1) : n((int)adj.size() - 1), adj(adj) {
siz.resize(n + 1), top.resize(n + 1), dep.resize(n + 1), in.resize(n + 1), out.resize(n + 1), seq.resize(n + 1), fa.resize(n + 1);
dep[root] = 0, top[root] = root;
dfs_siz(root), dfs_hld(root);
}
void dfs_siz(int u) {
if (fa[u] != 0) {
adj[u].erase(std::find(adj[u].begin(), adj[u].end(), fa[u]));
}
siz[u] = 1;
for (auto &v : adj[u]) {
fa[v] = u;
dep[v] = dep[u] + 1;
dfs_siz(v);
siz[u] += siz[v];
if (siz[v] > siz[adj[u][0]]) {
std::swap(v, adj[u][0]);
}
}
}
void dfs_hld(int u) {
in[u] = ++ times;
seq[in[u]] = u;
for (auto v : adj[u]) {
top[v] = v == adj[u][0] ? top[u] : v;
dfs_hld(v);
}
out[u] = times;
}
int lca(int u, int v) {
while (top[u] != top[v]) {
dep[top[u]] > dep[top[v]] ? u = fa[top[u]] : v = fa[top[v]];
}
return dep[u] < dep[v] ? u : v;
}
int dist(int u, int v) { return dep[u] + dep[v] - 2 * dep[(lca(u, v))]; }
};
struct DSU {
std::vector<int> p, siz;
DSU(int n) : p(n), siz(n, 1) { std::iota(p.begin(), p.end(), 0); }
int leader(int x) {
while (x != p[x]) x = p[x] = p[p[x]];
return x;
}
bool same(int x, int y) { return leader(x) == leader(y); }
bool merge(int x, int y) {
x = leader(x), y = leader(y);
if (x == y) return false;
siz[x] += siz[y], p[y] = x;
return true;
}
int size(int x) { return siz[leader(x)]; }
};
main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n;
std::cin >> n;
std::vector<std::vector<int>> g(n + 1);
for (int i = 0; i < n - 1; ++i) {
int a, b;
std::cin >> a >> b;
g[a].emplace_back(b);
g[b].emplace_back(a);
}
int r, t0;
std::cin >> r >> t0;
HLD hld(g, r);
DSU dsu(n + 1);
std::vector<int> ord(n + 1);
std::iota(ord.begin(), ord.end(), 0);
std::sort(ord.begin(), ord.end(),
[&](int x, int y) {
return hld.dep[x] < hld.dep[y];
});
std::vector<std::array<int, 2>> f(n + 1);
for (int i = 1; i <= n; ++i) {
f[i] = {i, i};
}
int max = 0;
auto add = [&](int a, int b) {
a = dsu.leader(a), b = dsu.leader(b);
std::vector<int> p{f[a][0], f[a][1], f[b][0], f[b][1]};
std::array<int, 2> res;
int maxd = -1;
for (int i = 0; i < 4; ++i) {
for (int j = i + 1; j < 4; ++j) {
int dist = hld.dist(p[i], p[j]);
if (dist > maxd) {
res = {p[i], p[j]};
maxd = dist;
}
}
}
max = std::max(max, maxd);
dsu.merge(a, b);
f[dsu.leader(a)] = res;
};
int mdep = hld.dep[ord[n]];
debug(mdep);
std::vector<int> dia(mdep + 1);
for (int depth = 1, idx = 2; depth <= mdep; ++depth) {
while (idx <= n && hld.dep[ord[idx]] == depth) {
add(r, ord[idx]);
dia[depth] = std::max(dia[depth], max);
idx += 1;
}
}
debug(dia);
std::vector<int> ans(n + 1);
for (int k = 1; k <= n; ++k) {
auto check = [&](int times) {
int depth = std::min(mdep, times);
int radius = (dia[depth] + 1) / 2;
return k * (times - t0) >= radius;
};
int l = t0, r = 2 * n + 1;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
ans[k] = r;
}
for (int i = 1; i <= n; ++i) {
std::cout << ans[i] << " \n"[i == n];
}
}