树网的核[有关树的直径]

 

输入样例#1: 

5 2
1 2 5
2 3 2
2 4 4
2 5 3

输出样例#1: 

5

输入样例#2: 

8 6
1 3 2
2 3 2 
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3

输出样例#2: 

5

n<=500000


分析

为了方便,st,ed表示直径两端的两个点

首先来看树的核是一个点的情况


证明: 使偏心距最大的点一定是st或ed

若x的偏心距最大,那么直径应该就是x--ed了


同理,当核是一条路径时,x上头和y下头的所有点都是不行的,所以这种情况的偏心距应该为

Max(x到st的距离,y到ed的距离,(x,y)之间任意一个节点到以该节点为根的最远距离


因此我们先dfs出st,然后再dfs出到dis的距离已经ed

然后一个一个往上找s个长度(x,y) 然后更新答案,最后再dfs出直径上的点到自己子树的最大距离然后更新答案


#include<bits/stdc++.h>
#define N 100005
using namespace std;
int first[N],next[N*2],to[N*2],w[N*2],tot;
int n,s,st,ed,ans=0x3fffffff,vis[N];
int dep[N],fa[N],dis[N],dis_st[N],fa_st[N];
int read(){
	int cnt=0;char ch=0;
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar();
	return cnt;
}
void add(int x,int y,int z){
	next[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
}
void dfs(int u){
	vis[u]=1;
	for(int i=first[u];i;i=next[i]){
		int t=to[i]; if(vis[t]) continue;
		dis[t]=dis[u]+w[i],fa[t]=u,dep[t]=dep[u]+1;
		if(dis[t]>dis[st]) st=t;
		dfs(t);
	}
}
void dfs_st(int u,int f){
	for(int i=first[u];i;i=next[i]){
		int t=to[i]; if(t==f)continue;
		dis_st[t]=dis_st[u]+w[i];
		fa_st[t]=u; 
		if(dis_st[t]>dis_st[ed]) ed=t;
		dfs_st(t,u);
	}
}
int main(){
	freopen("1.in","r",stdin);
	n=read(),s=read();
	for(int i=1;i<n;i++){
		int x=read(),y=read(),z=read();
		add(x,y,z),add(y,x,z);
	}
	dep[1]=1,dfs(1); 
	dfs_st(st,0);
	memcpy(dis,dis_st,sizeof(dis_st));
	memcpy(fa,fa_st,sizeof(fa_st));
	memset(vis,0,sizeof(vis));
	for(int x=ed,y=ed;x!=0;x=fa[x]){
		while(y&&dis[x]-dis[fa[y]]<=s) y=fa[y];
		ans=min(ans,max(dis[y],dis[ed]-dis[x]));
		vis[x]=1;
	}
	for(int i=ed;i;i=fa[i]) dis[i]=0,dfs(i);
	for(int i=1;i<=n;i++) ans=max(ans,dis[i]);
	cout<<ans;return 0;
}

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/83098829