题目
题解
首先吐槽一发题意
选择的路径可以不包含边???
只包含一个节点也算路径???
惹不起 惹不起
然后我们就可以发现,离任意一点最远的点必然是直径的端点,所以所选路径至少有一点在树的直径上
此时答案即为较远端点到当前点的距离
剩下的点若不在直径上,无法更新直径端点到所选路径的距离,答案不变
所以整条路径都在直径上答案是最优的
于是二分
(树的直径两遍bfs求得)
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 300005;
const int M = 2 * N;
inline int read()
{
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
return x * f;
}
int n, len;
struct node
{
int fr, to, w, nt;
node(int ff = 0, int tt = 0, int ww = 0, int nn = 0) {fr = ff; to = tt; w = ww; nt = nn;}
}E[M];
int num, p[N];
void add(int x, int y, int z) {E[++num] = node(x, y, z, p[x]); p[x] = num;}
int dis[N], from[N]; queue<int> q; bool mark[N];
void bfs(int s)
{
memset(dis, -1, sizeof(dis));
q.push(s); dis[s] = 0;
while(!q.empty())
{
int x = q.front(); q.pop();
for(int e = p[x]; e; e = E[e].nt)
{
int k = E[e].to;
if(dis[k] != -1) continue;
from[k] = x; q.push(k);
if(mark[E[e].to]) dis[k] = dis[x];
else dis[k] = dis[x] + E[e].w;
}
}
}
int dia, st[N];//st[i]记录i到s的距离
void get_dia()
{
int s = 0, t = 0;
bfs(1);
for(int i = 1; i <= n; i++) if(dis[i] > dis[s]) s = i;
bfs(s);
for(int i = 1; i <= n; i++) if(dis[i] > dis[t]) t = i;
dia = dis[t];
st[++st[0]] = dis[t]; mark[t] = true;
while(t != s)
{
st[++st[0]] = dis[from[t]];
t = from[t]; mark[t] = true;
}
bfs(s);
}
bool check(int mid)
{
int l = 1, r = st[0];
while(st[1] - st[l + 1] <= mid && l <= st[0]) l++;
while(st[r - 1] <= mid && r >= 1) r--;
return st[l] - st[r] <= len;
}
int main()
{
n = read(); len = read();
for(int i = 1; i < n; i++)
{
int x = read(), y = read(), z = read();
add(x, y, z); add(y, x, z);
}
get_dia();
int l = 0, r = dia, ans = 0;
for(int i = 1; i <= n; i++) ans = max(ans, dis[i]);
if(len < dia)
while(l <= r)
{
int mid = (l + r) >> 1;
if(check(mid)) {ans = mid; r = mid - 1;}
else l = mid + 1;
}
printf("%d", ans);
return 0;
}