题意
题解
设直径的两个端点为 u , v u,v u,v。根据直径的最长性,任何从 u , p u,p u,p 之间分叉离开直径的子树,其最远点与 p p p 的距离都不会比 p p p 与 u u u 的距离更远。
在树的直径的分叉上建立枢纽,设路径与树的直径的交点为 p p p,则 p p p 到直径端点 u , v u,v u,v 的路径至少有一条未被覆盖。根据树的直径的最长性,此时不会改变其他节点到所选择路径的距离的最大值。
那么只考虑在树的直径上建立枢纽即可。 m x [ i ] mx[i] mx[i] 代表树的直径上的节点 i i i 在不经过直径上其他节点的情况下能到达的最远点的距离,设满足条件的直径上的路径左右端点为 l , r l,r l,r,则答案为 max ( max i ∈ [ l , r ] { m x [ i ] } , d i s ( u , l ) , d i s ( r , v ) ) \max\Big(\max_{i\in [l,r]}\{mx[i]\},dis(u,l),dis(r,v)\Big) max(i∈[l,r]max{ mx[i]},dis(u,l),dis(r,v)) 根据树的直径的最长性,上式可简化为 max ( max i ∈ [ u , v ] { m x [ i ] } , d i s ( u , l ) , d i s ( r , v ) ) \max\Big(\max_{i\in [u,v]}\{mx[i]\},dis(u,l),dis(r,v)\Big) max(i∈[u,v]max{ mx[i]},dis(u,l),dis(r,v)) 使用尺取法更新答案即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 300005, maxm = maxn << 1, inf = 0x3f3f3f3f;
int N, S, num, ds[maxn], fa[maxn], mx[maxn], rec[maxn];
int tot, head[maxn], to[maxm], cost[maxm], nxt[maxm];
bool in[maxn];
inline void add(int x, int y, int z)
{
to[++tot] = y, cost[tot] = z, nxt[tot] = head[x], head[x] = tot;
}
void dfs(int x, int f, int w, int &t)
{
ds[x] = w, fa[x] = f;
if (ds[x] >= ds[t])
t = x;
for (int i = head[x]; i; i = nxt[i])
{
int y = to[i], z = cost[i];
if (y != f)
dfs(y, x, w + z, t);
}
}
void dfs2(int x, int f, int w)
{
mx[x] = w;
for (int i = head[x]; i; i = nxt[i])
{
int y = to[i], z = cost[i];
if (y != f && !in[y])
dfs2(y, x, w + z), mx[x] = max(mx[x], mx[y]);
}
}
int main()
{
scanf("%d%d", &N, &S);
for (int i = 1, x, y, z; i < N; ++i)
scanf("%d%d%d", &x, &y, &z), add(x, y, z), add(y, x, z);
int s = 1, t = 0;
dfs(s, 0, 0, t);
s = t, t = 0;
dfs(s, 0, 0, t);
int dm = ds[t];
for (int i = t; i; i = fa[i])
rec[++num] = i, in[i] = 1;
for (int i = 1; i <= num; ++i)
dfs2(rec[i], 0, 0);
int a = 0, res = inf;
for (int i = 1; i <= num; ++i)
a = max(a, mx[rec[i]]);
int l = 1, r = 1;
for (;;)
{
while (r + 1 <= num && ds[rec[l]] - ds[rec[r + 1]] <= S)
++r;
res = min(res, max(a, max(dm - ds[rec[l]], ds[rec[r]])));
if (r == num)
break;
++l;
}
printf("%d\n", res);
return 0;
}