ZOJ3820 Building Fire Stations 题解
ZOJ3820 Building Fire Stations 题解
题目链接
原 OJ(卡空间):https://pintia.cn/problem-sets/91827364500/exam/problems/91827369872
或者 Codeforces GYM(空间足够):https://codeforces.com/gym/100554/problem/B
有翻译+我自己出的数据(不卡空间):https://www.luogu.com.cn/problem/U370080
题目简述
一棵树,在其中找两个点,使得其他点到这两个的距离的较小值的最大值的最小值及其方案。
分析
先考虑选一个点的情况
树的中心模板,可以见我的帖子:https://www.cnblogs.com/RainPPR/p/tree-dp.html
两个点呢
假设我们已经选出了两个点 \((a,b)\) 那么,显然,树中所有的点要么去 \(a\) 要么去 \(b\),且一定有一条边作为分界线;即有一条边 \((p,q)\) 其两边子树中的点,都去 \(a\) 或都去 \(b\);且点 \(a\) 和点 \(b\) 一定不在同一棵子树中。
因此,我们就可以选点 \(a\) 为 \(p\) 一侧的子树的中心,点 \(b\) 为 \(q\) 一侧的子树的中心。然后考虑 \((p,q)\) 怎么选?(感性理解)可以大胆猜测,边 \((p,q)\) 一定是树的直径中,中间的那一条边!
为什么呢?与我在上面那篇帖子中树的中心的证明类似:一棵树上到点 \(x\) 最远的点一定是其直径 \((S,T)\) 的一个端点,根据三角不等式 \(\text{dis}(S,x)+\text{dis}(x,T)\ge\text{dis}(S,T)\),所以 \(\max\{\text{dis}(S,x),\text{dis}(x,T)\}\ge\frac{1}{2}\text{dis}(S,T)\),而等号是在 \(x\) 为 \((S,T)\) 中点时取到。
我们可以得出:\(\max\{\text{dis}(S,p),\text{dis}(q,T)\}\le\frac{1}{2}\text{dis}(S,T)\quad(\text{map}_{p,q}=1)\),因此有 \((p,q)\) 是 \((S,T)\) 上最中间的一段。
证明结束。
程序
注意,原数据会爆栈,所以不能用 DFS,可以换成 BFS(但是 Codeforces GYM 上空间加了一倍,到了 \(256\text{ MB}\),这就卡卡能过了)。
代码:
#include <bits/stdc++.h> using namespace std; #define rr read() inline int read() { int num = 0, flag = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') flag = -1; for (; isdigit(ch); ch = getchar()) num = num * 10 + ch - '0'; return num * flag; } #define tp(t) template<typename t> #define max _max tp(t) inline t _max(const t a, const t b) { return a > b ? a : b; } #define min _min tp(t) inline t _min(const t a, const t b) { return a < b ? a : b; } #define abs _abs tp(t) inline t _abs(const t x) { return x < 0 ? -x : x; } using pii = pair<int, int>; using tii = tuple<int, int>; const int N = 2e5 + 10; int n; vector<int> g[N]; void add(int u, int v) { g[u].push_back(v), g[v].push_back(u); } int lc, rc; int d[N], f[N]; #define get find_bfs::find // 在这里切换 DFS 和 BFS namespace find_bfs { // 返回距离 u 最远的点 pii q[N]; int st, ed; int find(int u) { d[u] = 0, f[u] = -1; st = 0, ed = 0, q[ed++] = {u, -1}; int c = u; while (st < ed) { pii now = q[st++]; int u = now.first; for (int v : g[u]) if (v != now.second) { if ((u == lc && v == rc) || (u == rc && v == lc)) continue; q[ed++] = {v, u}; f[v] = u; if ((d[v] = d[u] + 1) > d[c]) c = v; }} return c; } }; namespace find_dfs { // 返回距离 u 最远的点 int c; void dfs(int u, int fa) { for (int v : g[u]) if (v != fa) { if ((u == lc && v == rc) || (u == rc && v == lc)) continue; if ((d[v] = d[u] + 1) > d[c]) c = v; dfs(v, u); f[v] = u; }} int find(int u) { d[u] = 0, f[u] = -1, c = u; dfs(u, -1); return c; } } int main() { int T = rr; while (T--) { n = rr; for (int i = 1; i < n; ++i) add(rr, rr); lc = rc = -1; int s = get(1), e = get(s); lc = e, rc = f[e]; for (int i = 1; i < d[e]; i += 2) tie(lc, rc) = make_tuple(rc, f[rc]); int le = get(get(lc)), ls = d[le] + 1; for (int i = d[le]; i > 1; i -= 2) le = f[le]; int re = get(get(rc)), rs = d[re] + 1; for (int i = d[re]; i > 1; i -= 2) re = f[re]; printf("%d %d %d\n", max(ls, rs) >> 1, le, re); for (int i = 1; i <= n + 1; ++i) g[i].clear(); } return 0; }
本文来自博客园,作者:RainPPR,转载请注明原文链接:https://www.cnblogs.com/RainPPR/p/solution-zoj3820.html
如有侵权请联系我(或 2125773894@qq.com)删除。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!