先求带权树直径,再用双指针枚举直径上的长度不超过 s 的核心区间。
OJ: luogu
题目 ID: P1099
难度:提高
标签:树的直径双指针贪心python
日期: 2026-07-17 02:00
题意
在带权树的一条直径上选长度不超过 s 的路径,使所有节点到它的最大距离最小。
思路
两次树上最远点搜索得到直径端点 A,B。对直径节点 k,直径外分支深度不会超过它到任一端点的距离;因此区间 [i,j] 的偏心距是 max(dist(A,i), dist(j,B), 最大分支深度)。直径前缀距离单调,双指针找每个 i 能到达的最右 j。
Python 知识
for node in order可以遍历一个不断 append 的列表,适合非递归树遍历。max(order, key=distance.__getitem__)按数组值找最远点。- 字典
position把直径节点集合变成O(1)的分支判断。
代码
python
import sys
input = sys.stdin.buffer.readline
n, limit = map(int, input().split())
graph = [[] for _ in range(n + 1)]
for _ in range(n - 1):
u, v, weight = map(int, input().split())
graph[u].append((v, weight))
graph[v].append((u, weight))
def farthest(start):
parent = [0] * (n + 1)
distance = [0] * (n + 1)
order = [start]
for node in order:
for neighbor, weight in graph[node]:
if neighbor != parent[node]:
parent[neighbor] = node
distance[neighbor] = distance[node] + weight
order.append(neighbor)
end = max(order, key=distance.__getitem__)
return end, parent, distance
diameter_start, _, _ = farthest(1)
diameter_end, parent, distance = farthest(diameter_start)
diameter = []
node = diameter_end
while node:
diameter.append(node)
if node == diameter_start:
break
node = parent[node]
diameter.reverse()
position = {node: index for index, node in enumerate(diameter)}
coordinate = [distance[node] for node in diameter]
branch_maximum = 0
for root in diameter:
stack = [(root, 0, 0)]
while stack:
node, previous, branch_distance = stack.pop()
branch_maximum = max(branch_maximum, branch_distance)
for neighbor, weight in graph[node]:
if neighbor != previous and neighbor not in position:
stack.append((neighbor, node, branch_distance + weight))
diameter_length = coordinate[-1]
answer = diameter_length
right = 0
for left in range(len(diameter)):
right = max(right, left)
while right + 1 < len(diameter) and coordinate[right + 1] - coordinate[left] <= limit:
right += 1
answer = min(answer, max(coordinate[left], diameter_length - coordinate[right], branch_maximum))
print(answer)原有 C++ 版本仍保留:
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-17 01:04
* update_at: 2026-07-17 01:04
*/
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
return 0;
}复杂度
时间 O(n),空间 O(n)。
总结
带权树的核问题先降到唯一的直径,再把长度约束转成单调双指针。
