【HDU2196】Computer

题目大意:给定一棵 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;
}

转载于:https://www.cnblogs.com/wzj-xhjbk/p/10887704.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值