题目大意:给定一棵 N 个节点的无根树,边有边权。求每个节点到树上其他节点距离的最大值是多少,距离定义为两个节点之间路径的边权和。
题解:首先,进行一次 dfs 求出每个节点到其子树中点的距离的最大值和次大值,最大值用 \(dp[u][0]\) 表示,次大值用 \(dp[u][1]\) 表示。发现对于树上任意一个节点的最远距离一定为该节点到其子树中节点距离的最大值或该节点到其子树外节点距离的最大值。因此,现在问题转化为如何在 \(O(n)\) 的时间内求得每个节点到以该节点为根的子树外的最大距离。用 \(dp[u][2]\) 表示这个值,若当前节点是该点的父节点在其子树中最长链的节点,则有 \[dp[u][2]=max(dp[fa][2]+w,dp[fa][1]+w)\],否则有 \[dp[u][2]=max(dp[fa][2]+w,dp[fa][0]+w)\]
代码如下
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
typedef long long LL;
int n;
LL dp[maxn][3];
struct node{
int nxt,to; LL w;
}e[maxn<<1];
int tot=1,head[maxn];
inline void add_edge(int from,int to,LL w){
e[++tot]=node{head[from],to,w},head[from]=tot;
}
void dfs1(int u,int fa){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to; LL w=e[i].w;
if(v==fa)continue;
dfs1(v,u);
if(dp[u][0]<dp[v][0]+w){
dp[u][1]=dp[u][0];
dp[u][0]=dp[v][0]+w;
}else{
dp[u][1]=max(dp[u][1],dp[v][0]+w);
}
}
}
void dfs2(int u,int fa){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to; LL w=e[i].w;
if(v==fa)continue;
if(dp[u][0]==dp[v][0]+w)dp[v][2]=max(dp[u][1]+w,dp[u][2]+w);
else dp[v][2]=max(dp[u][0]+w,dp[u][2]+w);
dfs2(v,u);
}
}
void read_and_parse(){
for(int i=2;i<=n;i++){
int fa;LL len;
scanf("%d%lld",&fa,&len);
add_edge(i,fa,len),add_edge(fa,i,len);
}
}
void solve(){
dfs1(1,0),dfs2(1,0);
for(int i=1;i<=n;i++)printf("%lld\n",max(dp[i][0],dp[i][2]));
}
void init(){
memset(dp,0,sizeof(dp));
memset(head,0,sizeof(head)),tot=1;
}
int main(){
while(scanf("%d",&n)!=EOF){
init();
read_and_parse();
solve();
}
return 0;
}