题目描述
设T=(V, E, W) 是一个无圈且连通的无向图(也称为无根树),每条边带有正整数的权,我们称T为树网(treenetwork),其中V, E分别表示结点与边的集合,W表示各边长度的集合,并设T有n个结点。路径:树网中任何两结点a,b都存在唯一的一条简单路径,用d(a,b)表示以a,b为端点的路径的长度,它是该路径上各边长度之和。我们称d(a,b)为a,b两结点间的距离。一点v到一条路径P的距离为该点与P上的最近的结点的距离: d(v,P)=min{d(v,u),u为路径P上的结点}。树网的直径:树网中最长的路径称为树网的直径。对于给定的树网T,直径不一定是唯一的,但可以证明:各直径的中点(不一定恰好是某个结点,可能在某条边的内部)是唯一的,我们称该点为树网的中心。偏心距ECC(F):树网T中距路径F最远的结点到路径F的距离,即。 任务:对于给定的树网T=(V,E,W)和非负整数s,求一个路径F,它是某直径上的一段路径(该路径两端均为树网中的结点),其长度不超过s(可以等于s),使偏心距ECC(F)最小。我们称这个路径为树网T=(V,E,W)的核(Core)。必要时,F可以退化为某个结点。一般来说,在上述定义下,核不一定只有一个,但最小偏心距是唯一的。下面的图给出了树网的一个实例。图中,A-B与A-C是两条直径,长度均为20。点W是树网的中心,EF边的长度为5。如果指定s=11,则树网的核为路径DEFG(也可以取为路径DEF),偏心距为8。如果指定s=0(或s=1、s=2),则树网的核为结点F,偏心距为12。
提示
对于70%的数据,n<=200000
对于100%的数据:n<=500000, s<2^31, 所有权值<500
解析:这个题的数据如果和noip一样小就怎么写都行了,然而500000的数据只能考略O(n)或O(logn)的复杂度。分析这道题可以得到这样一个性质:根据直径最长性,如果一点与直径的连线的交点在F外,那么这个点与F的距离一定不是偏心距。有了这个性质,这道题就可以以O(n)的复杂度用单调队列做出来了。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
deque<int>q;
struct point
{
int to;
int next;
int dis;
}e[1000010];
int n,x,y,z,res=100000010,maxn,s,t,num,ss;
int d[500001],head[500001],f[500001],pre[500001];
bool vis[500001];
void add(int from,int to,int dis)
{
e[++num].next=head[from];
e[num].to=to;
e[num].dis=dis;
head[from]=num;
}
void dfs(int x)
{
vis[x]=true;
for(int i=head[x];i!=0;i=e[i].next)
{
int to=e[i].to;
if(!vis[to])
{
d[to]=d[x]+e[i].dis;
dfs(to);
}
}
}
void dfs1(int x)
{
vis[x]=true;
for(int i=head[x];i!=0;i=e[i].next)
{
int to=e[i].to;
if(!vis[to])
{
pre[to]=x;
d[to]=d[x]+e[i].dis;
dfs1(to);
}
}
}
void find(int x)
{
vis[x]=true;
for(int i=head[x];i!=0;i=e[i].next)
{
int to=e[i].to;
if(!vis[to])
{
find(to);
d[x]=max(d[x],d[to]+e[i].dis);
}
}
}
void getf()
{
int slr=0;
for(int i=t;i!=0;i=pre[i])
{
f[i]=slr;
for(int j=head[i];j!=0;j=e[j].next)
{
int to=e[j].to;
if(pre[i]==to)
{
slr+=e[j].dis;
break;
}
}
}
}
int main()
{
scanf("%d%d",&n,&ss);
for(int i=1;i<=n-1;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dfs(1);
for(int i=1;i<=n;i++)
{
if(d[i]>maxn)
{
maxn=d[i];
s=i;
}
}
memset(vis,false,sizeof(vis));
memset(d,0,sizeof(d));
dfs1(s);
maxn=0;
for(int i=1;i<=n;i++)
if(d[i]>maxn)
{
maxn=d[i];
t=i;
}
memset(vis,false,sizeof(vis));
memset(d,0,sizeof(d));
for(int i=t;i!=0;i=pre[i])
vis[i]=true;
for(int i=t;i!=0;i=pre[i])
find(i);
getf();
int r=t,l=pre[t];
q.push_front(l);
if(d[l]<d[r])
q.push_back(r);
while(1)
{
if(pre[l]!=0&&f[pre[l]]-f[r]<=ss)
{
l=pre[l];
int ans=max(f[r],maxn-f[l]);
q.push_front(l);
while(d[q.back()]<d[l])
q.pop_back();
ans=max(ans,d[q.back()]);
res=min(res,ans);
}
else if(l!=r)
{
if(q.back()==r)
q.pop_back();
r=pre[r];
int ans=max(f[r],maxn-f[l]);
ans=max(ans,d[q.back()]);
res=min(res,ans);
}
else if(l==r&&pre[l])
{
while(!q.empty())
q.pop_back();
l=pre[l];
r=l;
q.push_front(l);
int ans=max(f[r],maxn-f[l]);
ans=max(ans,d[q.back()]);
res=min(res,ans);
}
else
break;
}
printf("%d",res);
return 0;
}