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]);
}