Problem 1999. -- [Noip2007]Core树网的核

树网的核

Problem 1999. -- [Noip2007]Core树网的核

1999: [Noip2007]Core树网的核

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 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

Sample Output

5

HINT

对于70%的数据,n<=200000
对于100%的数据:n<=500000, s<2^31, 所有权值<500

==============================================
似乎SPOJ上加强版的数据...

Source

[Submit][Status][Discuss]

HOME Back

题解

参照Yi Cai的题解。

首先明确几个性质:

  1. 对于树中的任意一点,距离其最远的点一定是树的直径的某一端点。
  2. 所有的直径是等价的,即任意一条所能求出的该最小偏心距相等。

于是我们可以用两次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;
}
原文地址:https://www.cnblogs.com/autoint/p/10922192.html