题意:在一棵树中选一条长度不超过s的路径,使其他所有城市到这条路径的最远距离最小。
题解:树的直径+单调队列
先求出树的直径,再用单调队列搞一搞就好了。
代码:
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<vector>
using namespace std;
int n,s,num=0,fst[300010],dis[300010],pre[300010],maxd[300010],dd[300010],ans=2147483647;
struct edge
{
int x,y,c,n;
}e[600010];
bool v[300010];
vector<int>d,en;
void ins(int x,int y,int c)
{
e[++num]={x,y,c,fst[x]};
fst[x]=num;
}
int bfs(int s)
{
static queue<int>q;
q.push(s);
memset(dis,63,sizeof(dis));
dis[s]=0;
v[s]=1;
int t=s;
memset(pre,0,sizeof(pre));
while(!q.empty())
{
int x=q.front();
v[x]=0;
if(dis[x]>dis[t])
t=x;
for(int i=fst[x];i;i=e[i].n)
{
int y=e[i].y;
if(!v[y]&&dis[y]>dis[x]+e[i].c)
{
dis[y]=dis[x]+e[i].c;
pre[y]=i;
v[y]=1;
q.push(y);
}
}
q.pop();
}
return t;
}
int main()
{
scanf("%d%d",&n,&s);
for(int i=1;i<n;i++)
{
int x,y,c;
scanf("%d%d%d",&x,&y,&c);
ins(x,y,c);
ins(y,x,c);
}
int st=bfs(1);
memset(v,0,sizeof(v));
int ed=bfs(st);
for(int i=ed;i;i=e[pre[i]].x)
{
v[i]=1;
d.push_back(i);
en.push_back(pre[i]);
}
for(int i=0;i<d.size();i++)
{
maxd[i]=dis[bfs(d[i])];
v[d[i]]=1;
}
dd[0]=0;
for(int i=0;i<d.size()-1;i++)
dd[i+1]=dd[i]+e[en[i]].c;/*
for(int i=0;i<d.size();i++)
printf("d:%d en:%d maxd:%d dd:%d\n",d[i],en[i],maxd[i],dd[i]);*/
int l=0;
deque<int>q;
for(int r=0;r<d.size();r++)
{
while(!q.empty()&&maxd[r]>q.back())
q.pop_back();
q.push_back(maxd[r]);
while(dd[r]-dd[l]>s)
{
if(maxd[l]==q.front())
q.pop_front();
l++;
}
ans=min(ans,max(max(max(dd[l],maxd[l]),max(dd[d.size()-1]-dd[r],maxd[r])),q.front()));
// printf("%d %d %d\n",l,r,ans);
}
printf("%d",ans);
}