Time Limit: 10 Sec Memory Limit: 512 MB
某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000)。
这个国家的人对火焰有超越宇宙的热情,所以这个国家最兴旺的行业是消防业。由于政府对国民的热情忍无可忍(大量的消防经费开销)可是却又无可奈何(总统竞选的国民支持率),所以只能想尽方法提高消防能力。
现在这个国家的经费足以在一条边长度和不超过s的路径(两端都是城市)上建立消防枢纽,为了尽量提高枢纽的利用率,要求其他所有城市到这条路径的距离的最大值最小。
你受命监管这个项目,你当然需要知道应该把枢纽建立在什么位置上。
Input
输入包含n行:
第1行,两个正整数n和s,中间用一个空格隔开。其中n为城市的个数,s为路径长度的上界。设结点编号以此为1,2,……,n。
从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2 4 7”表示连接结点2与4的边的长度为7。
Output
输出包含一个非负整数,即所有城市到选择的路径的最大值,当然这个最大值必须是所有方案中最小的。
Sample Input
【样例输入1】
5 2
1 2 5
2 3 2
2 4 4
2 5 3
【样例输入2】
8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3
Sample Output
【样例输出1】
5
【样例输出2】
5
HINT
对于100%的数据,n<=300000,边长小等于1000。
/*
证明1:对于直径上的点,离它最远的点一定是直径上的某个点
假设直径为ac
e
.
.
.
a.......b.....c(设ab>bc)
.
.
.
.
d
对于b点来说,离它最远的应该是a点
如果不是a,而是d,则(c,b,d)应该是直径
证明2:对于非直径上的点d, 离它最远的点也一定是直径上的某个点
假如离d最远的不是a,而是e.
则db+be>db+ab
于是be>ab,也就是说e离b,比a离b更远,这也开始的证明相违背。
l...........t.......i.........r
l,r为直径的左右端点,i为我们枚举的一个点,t为另一个点(初值为i的父亲点)
[t,i]的距离不超过规定的Len
则直径上没有被选中的,到选中的边的距离,按要求尽可能小,于是取
ans=min(ans,max(d[t],d[r]-d[i]));
d数组代表每个点到l的距离。
由于树上所有点,离它们最远的点,一定是直径的两个端点之一,所以一般来说这样求出来后就可以了
但是考虑到给定的Len可能覆盖整个路径。于是求出来的ans为0
于是我们还要将直径上的点一个个拿出来,求出其它不在直径上点到它们的距离
再找出这些距离的最大值, 再与ans取个最大值。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int fa[310100],vis[301000],last[301000],len=0,d[301000];
struct node
{
int to,next,w;
}a[601000];
void add(int a1,int a2,int a3)
{
len++;
a[len].to=a2;
a[len].w=a3;
a[len].next=last[a1];
last[a1]=len;
}
void dfs(int x)
{
for(int i=last[x];i;i=a[i].next)
{
int to=a[i].to;
if(vis[to]||fa[x]==to) continue;
fa[to]=x;
d[to]=d[x]+a[i].w;
dfs(to);
}
}
int main()
{
int n,s,x,y,z,ans=2147483647;
cin>>n>>s;
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
int l=1,r=1;
dfs(l);
for(int i=1;i<=n;i++)
if(d[i]>d[l])
l=i;
memset(fa,0,sizeof(fa));
d[l]=0;
dfs(l);//算出每个点到L的距离,l是直径的左端点
for(int i=1;i<=n;i++)
if(d[i]>d[r])
r=i;
int t=r; //找出直径的右端点
for(int i=r;i;i=fa[i])//尺取法
{
while(fa[t]&&d[i]-d[fa[t]]<=s)
t=fa[t];
ans=min(ans,max(d[t],d[r]-d[i]));
}
for(int i=r;i;i=fa[i]) //找出直径上的点
vis[i]=1;
for(int i=r;i;i=fa[i])
d[i]=0,dfs(i);//以直径上每个点为根,找出非直径上的点到它们的距离
for(int i=1;i<=n;i++) //在找出这些距离后,求出其最大值
if(vis[i]==0)
ans=max(ans,d[i]);
cout<<ans;
}