Page Top

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;
}
posted @   RainPPR  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示