树网的核
1999: [Noip2007]Core树网的核
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 2514 Solved: 855
[Submit][Status][Discuss]
Description
设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。
Input
包含n行:
第1行,两个正整数n和s,中间用一个空格隔开。其中n为树网结点的个数,s为树网的核的长度的上界。设结点编号依次为1, 2, ..., n。
从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2 4 7”表示连接结点2与4的边的长度为7。
所给的数据都是正确的,不必检验。
Output
只有一个非负整数,为指定意义下的最小偏心距。
Sample Input
5 2
1 2 5
2 3 2
2 4 4
2 5 3
1 2 5
2 3 2
2 4 4
2 5 3
Sample Output
5
HINT
对于70%的数据,n<=200000
对于100%的数据:n<=500000, s<2^31, 所有权值<500
==============================================
似乎SPOJ上加强版的数据...
Source
HOME Back
题解
参照Yi Cai的题解。
首先明确几个性质:
- 对于树中的任意一点,距离其最远的点一定是树的直径的某一端点。
- 所有的直径是等价的,即任意一条所能求出的该最小偏心距相等。
于是我们可以用两次dfs求出直径。任取一个点找到离它最远的点r,再从r找到距离它最远的点l。l到r的路径就是直径。
显然在长度不超过S的情况下,链最长最好。在l到r上维护尽可能长的链,找到左右端点到直径做右端点的较大值的最小值。然后由链上各个点出发,找到不经过直径上的点抵达的其他点的最大深度。这个最大深度和之前的最小值中较大的就是答案。
为什么是整条直径上找最大深度,而不是在核上找呢?
显然,如果这个深度最深的点不是从核中的点,那么它到核的距离必定小于核的端点到直径端点的距离。所以如果有个节点到核的距离小于核的端点到直径端点的距离,那么它必定是从核上延伸出去的。
时间复杂度(O(n))
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;
co int SIZE=500010;
int head[SIZE],ver[SIZE*2],Next[SIZE*2],edge[SIZE*2];
int fa[SIZE],f[SIZE],d[SIZE],a[SIZE];
int n,s,m,tot;
bool v[SIZE];
void add(int x,int y,int z){
ver[++tot]=y,Next[tot]=head[x],edge[tot]=z,head[x]=tot;
}
void dfs(int x){
for(int i=head[x];i;i=Next[i]){
if(ver[i]!=fa[x]){
fa[ver[i]]=x;
d[ver[i]]=d[x]+edge[i];
dfs(ver[i]);
}
}
}
void diameter(){
dfs(1);
int x=1;
for(int i=2;i<=n;++i)if(d[i]>d[x]) x=i;
d[x]=0;
memset(fa,0,sizeof fa);
dfs(x);
int y=1;
for(int i=2;i<=n;++i)if(d[i]>d[y]) y=i;
while(y!=x){
v[y]=1;
a[++m]=y;
y=fa[y];
}
v[x]=1;
a[++m]=x;
}
void treedp(int x){
v[x]=1;
for(int i=head[x];i;i=Next[i])
if(!v[ver[i]]){
treedp(ver[i]);
f[x]=max(f[x],f[ver[i]]+edge[i]);
}
}
int main(){
read(n),read(s);
for(int i=1,x,y,z;i<n;++i){
read(x),read(y),read(z);
add(x,y,z),add(y,x,z);
}
diameter();
for(int i=1;i<=m;++i) treedp(a[i]);
int ans=0x7fffffff,temp=0;
for(int i=1;i<=m;++i) temp=max(temp,f[a[i]]);
for(int i=m,j=m;i>=1;--i){
while(j>=1&&d[a[j]]-d[a[i]]<=s) --j;
ans=min(ans,max(temp,max(d[a[i]],d[a[1]]-d[a[j+1]])));
}
printf("%d
",ans);
return 0;
}