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)删除。