这个题要求最大值最小,那就是铁打的二分
然后考虑检验,可以根据二分值卡极限找距离然后判断是不是一条路径再判断路径和
这样其实dp就可以做了,但有更方便的做法,就是求出直径然后变成队列操作,
由于直径上的点最远的点都在直径上,所以就只用判断直径,
下限是非直径点到直径的最大距离
上限是直径长度
但注意有可能直径上的边每一条都比s大,这时需要扫一遍所有的边,判断这条边到直径两个端点的距离即可(数据没卡就没写)
码:
#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define N 600005
int fu[N],l,r,d[N],rt,zz,maxx,i,j,n,m,s,x,y,z,qsum[N],xx,ch[N],ans;
bool bj[N];
vector<int>v[N],c[N];
void dfs1(int o,int fa,int dis)
{
int i;
fu[o]=fa;
if(!rt){if(dis>maxx)maxx=dis,xx=o;}
else if(dis>maxx)maxx=dis,zz=o;
for(i=0;i<v[o].size();i++)
{int nd=v[o][i]; if(nd==fa)continue;
dfs1(nd,o,dis+c[o][i]);
}
}
void dfs2(int o,int fa)
{
d[o]=0;
int i,nd;
if(bj[o])
{
for(i=0;i<v[o].size();i++)
{
nd=v[o][i]; if(nd==fa)continue;
dfs2(nd,o);
if(bj[nd]==0)
l=max(l,d[nd]+c[o][i]);
}
}else
{
for(i=0;i<v[o].size();i++)
{
nd=v[o][i]; if(nd==fa)continue;
dfs2(nd,o);
d[o]=max(d[o],d[nd]+c[o][i]);
}
}
}
int main()
{
scanf("%d%d",&n,&s);
for(i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
v[x].push_back(y);
c[x].push_back(z);
v[y].push_back(x);
c[y].push_back(z);
r+=z;
}
dfs1(1,0,0);rt=xx;
maxx=0;
dfs1(rt,0,0);
int o=zz;
while(rt!=o){
bj[o]=1;
for(i=0;i<v[fu[o]].size();i++)
if(v[fu[o]][i]==o)ch[fu[o]]=o,qsum[fu[o]]=qsum[o]+c[fu[o]][i];
o=fu[o];
}
bj[o]=1;
dfs2(rt,0);
r++;
while(l<r)
{
int mid=(l+r)>>1;
int z1=rt,z2=zz;
while(z1!=z2&&qsum[rt]-qsum[ch[z1]]<=mid)z1=ch[z1];
while(z1!=z2&&qsum[fu[z2]]<=mid)z2=fu[z2];
if(z1==z2)r=mid;
if(qsum[z1]-qsum[z2]<=s)
{
ans=mid;
r=mid;
}else l=mid+1;
}
printf("%d",ans);
}