G - Computer HDU - 2196(树形 DP+dp 预处理)

第二遍做这一题了,很经典,附上一张盗来的图,

思路

  1. 首先用一个 dfs 去预处理每个节点 x,到以 x 为根的子树的叶子节点的最大、最小值,就是求树的直径的时候的那个 dp 求法,并且标记最大最小值的分别经过的事 x 的那个子节点,
  2. 然后再用另一个 DFS 函数去以当前点 x 为起点所能走的最长路径,如下图,如果 x=2 的时候我们可在这个树分成两部分,第一部分是以 x 为子树的那一部分,即蓝色圈着的部分,另一部分是剩余的部分,即红色线圈着的部分。
  3. 这两部分都有可能产生最大值,蓝色部分产色的最大值,可以通过 与处理的数据直接求的,而红色部分则可以通过,从上往下的递归过程维护得到,
  4. 从红、蓝两部分的最大值中选择一个最大值就是 x 节点的答案。
    在这里插入图片描述

题解

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <string>
#include <queue>
#include <map>
#include <bitset>
#include <vector>
using namespace std;
void fre() { system("clear"), freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { system("clear"), freopen("A.txt", "r", stdin);}
void Run(int x = 0) {     
#ifdef ACM  //宏定义免注释 freopen
    if (! x) fre(); else Fre();
#endif
}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define db double
#define ll long long
#define ull unsigned long long
#define Pir pair<ll, ll>
#define m_p make_pair
#define for_(i, s, e) for(ll i = (ll)(s); i <= (ll)(e); i ++)
#define rep_(i, e, s) for(ll i = (ll)(e); i >= (ll)(s); i --)
#define memset(a, b, c) memset(a, (int)b, c);
#define size() size() * 1LL
#define sc scanf
#define pr printf
#define sd(a) sc("%lld", &a)
#define ss(a) sc("%s", a)
#define __  pr( "------------------------\n" );
#define ___ pr("\n------------------------\n");
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define esp 1e-7
#define mod (ll)(1e9 + 7)
/*=========================ACMer===========================*/
const ll mxn = 1e4 + 10;
struct Edge
{
    ll v, w, next;
} edge[mxn << 1];
ll head[mxn], tot;
Pir f[mxn][2];

void Add(ll u, ll v, ll w)
{
    edge[++ tot] = (Edge){ v, w, head[u] };
    head[u] = tot;
}

void pre_dfs(ll u, ll p)
{
    for (ll i = head[u]; i; i = edge[i].next)
    {
        ll v = edge[i].v, w = edge[i].w;
        if(v == p) continue;
        pre_dfs(v, u);
        ll tmp = f[v][0].fi + w;
        if(tmp >= f[u][0].fi) f[u][1] = f[u][0], f[u][0] = m_p(tmp, v);
        else if(tmp > f[u][1].fi) f[u][1] = m_p(tmp, v);
    }
}

ll dp[mxn];
void dfs(ll u, ll p, ll pmx)
{
    for (ll i = head[u]; i; i = edge[i].next)
    {
        ll v = edge[i].v, w = edge[i].w;
        if(v == p) continue;
        ll fa_mx = 0;
        if(f[u][0].se != v) fa_mx = f[u][0].fi;
        else if(f[u][1].se != v) fa_mx = f[u][1].fi;
        ll now_mx = max(fa_mx, pmx) + w;
        dp[v] = max(f[v][0].fi, now_mx);
        dfs(v, u, now_mx);
    }
}

void init(ll n)
{
    memset(f, 0, sizeof f);
    memset(head, 0, sizeof head);
    tot = 0;
}

int main()
{
    Run();
    ll n;
    while (sd(n) != EOF)
    {
        init(n);
        ll x, y;
        for_(i, 2, n)
        {
            sc("%lld %lld", &x, &y);
            Add(i, x, y);
            Add(x, i, y);
        }
        pre_dfs(1, 0);
        dfs(1, 0, 0);
        dp[1] = max(f[1][0].fi, f[1][1].fi);        //单独计算一下 1 根节点
        for_(i, 1, n) pr("%lld\n", dp[i]);
    }

    return 0;
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值