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];
    }
}
posted @ 2024-06-01 13:16  sleeeeeping  阅读(68)  评论(0编辑  收藏  举报