2020.7.16 T3 【NOIP2015模拟10.29B组】树上路径(jz暑假训练day2)

Description

现在有一棵n个点的无向树,每个点的编号在1-n之间,求出每个点所在的最长路。

Input

输入文件名为tree.in。
第一行为一个整数n。
之后n-1行,每行三个整数u,v,w,分别表示一条边连的两个点和边权。

Output

输出文件tree.out,共n行,分别表示经过每个点的最长路。

Sample Input

4
1 2 3
1 3 4
1 4 2

Sample Output

7
7
7
6

Data Constraint

对于50%,1<=n<=1000
对于100%,1<=n<=100000,每个点的度不超过30,1<=u,v<=n,1<=w<=10000

赛时

刚开始没想码,以为要什么lca之类的,结果剩20秒码完交上去就神奇的a了!!!
正解
许多方法,自己就是老老实实dfs
(1)dfs求出每个节点(根为1)向下最长的路径,这里记录前2个。
(2)再一次dfs统计ans
步骤就这样,那么说一些细节。
关于(1),注意两个记录的路径的任意经过的节点0的lca都是此节点。也就说除了这个节点,一个节点不可能出现在两个路径上,至于为什么是因为这两个路径以后要用来计算答案,若有重复的话是不合法的。
关于(2),如何统计答案呢,分几种情况:
首先说明当我们遍历到x时,我们累计的是son[x]的ans,所以x的ans在它的父亲遍历时就算出来了。
首先,对于点x的路径,它一定是从入点进来出点出去,那么出点就一种情况,就是第一长路径,因为只有这个最优。然后入点有两种,一是从父亲进来,一种是从另一个子节点进来,若是第二种情况,那么最优就是第二长路径进来。那么如果是从父亲进来的话,又有两种情况,一是从祖父进来,另一个是从父亲的另一个子节点进来(也就是x的兄弟)
好了,一个一个来。
首先ans必须有个第一长路径。然后考虑入度:
如果x属于fa的第一长路径上的点:
1.另一个子节点过来,那么ans=lon1[x]+lon2[x](lon1为第一路径,lon2为第二路径)
2.从父亲进来,并且是从父亲子节点来的,那么ans=lon1[x]+lon2[fa]+w(为什么是lon2[fa]不是lon1[fa]呢,因为x属于lon1[fa],所以不行,然后w指fa与x的边权)
3.从父亲进来,并且是从父亲父亲节点来的,那么ans=lon1[x]+res+w(res是从父亲节点传进来的,等会说怎么求)
如果x不属于fa的第一长路径的点:
其他同理,就是2的情况,ans=lon1[x]+lon1[fa]+w
ans要取最大值
ok,这些都搞定了,那么还差一个res。实际上呢,对于fa传入x的res,就是ans-lon1[x],那么为啥要-lon1[x]呢?因为lon1[x]是一直深入到叶子的,那么算上去的话,等求x的son的答案时,就会重复而且不是最优了。
剩下的看看码吧

#include<cstdio>
#include<iostream>
#define N 100007
using namespace std;
struct node{
	int nxt,to,w;
}e[N<<1];//链式前向星
int head[N<<2],cnt;
void add(int u,int v,int w){//建边
	e[++cnt].to=v;
	e[cnt].w=w;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
int f[N],dis[N],lon1[N],lon2[N];//f为父亲节点,dis为答案,lon1,lon2为第一,第二长的路径
int n;
void dfs(int fa,int x){
	f[x]=fa;
	for(int i=head[x];i;i=e[i].nxt){
		int v=e[i].to;
		if(f[x]==v) continue;//遇到父亲就退
		dfs(x,v);
		if(lon1[x]<lon1[v]+e[i].w){//当子节点的lon1+边权比x的第一路径长时,
			lon2[x]=lon1[x];//第一路径成为第二,同时这里没有判断lon2[v]+e[i].w>lon2[x],因为就算如此,第二路径也不能再让v的路径当了
			lon1[x]=lon1[v]+e[i].w;
		}else if(lon2[x]<lon1[v]+e[i].w)//这里就是同理
			lon2[x]=lon1[v]+e[i].w;
	}
}
void find(int res,int x){
	for(int i=head[x];i;i=e[i].nxt){
		int v=e[i].to;
		if(f[x]==v) continue;
		if(lon1[x]!=lon1[v]+e[i].w){//父亲的最长路径没经过x时
			int max1=max(max(res+e[i].w,lon1[x]+e[i].w),lon2[v]);//最优答案
			dis[v]=max1+lon1[v];
			find(max1,v);//max1也就是ans-lon1[v]
		}else{
			int max1=max(max(res+e[i].w,lon2[x]+e[i].w),lon2[v]);//同理
			dis[v]=max1+lon1[v];
		find(max1,v);
		}
	}
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<n;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);
		add(v,u,w);
	}
	dfs(0,1);//父亲;当前点
	find(0,1);//上面传的;当前点
	dis[1]=lon1[1]+lon2[1];//根节点的ans必定时第一第二路径之和
	for(int i=1;i<=n;i++)
		printf("%d\n",dis[i]);
}

猜你喜欢

转载自blog.csdn.net/jay_zai/article/details/107390634
今日推荐