解题思路

  • 二叉树遍历通常利用递归的思想,粗略地写了一版(链接前向星存图),令ansans表示二叉树中的最大路径和,ansians_i表示以nodeinode_i为节点的最大路径和,那么ansans必定是ansians_i中的某一个。
  • 在遍历的过程中,对于任意一个节点ii的left分支和right分支,记录其较大者mxmx和较小者mnmn(注意不一定存在),ans=max(ans,wi,mx,wi+mx,wi+mn+mx)ans = \max(ans, w_i,mx,w_i+mx,w_i+mn + mx)
  • 利用dpidp_i记录经过nodeinode_i的到某一节点的较大分支,则有dpi=max(dp[i],wi+mx)dp_i = \max(dp[i],w_i+mx),用于i的father计算经过其的最大路径和。
  • 后续再给出优化的代码。
//粗糙的版本。
#include<bits/stdc++.h>
using namespace std;
struct edge{
    int to, next, w;
}es[100005];
int head[100005];
int ans = INT_MIN, cnt = 0;
void add_edge(int from, int to){
    es[++cnt].next = head[from];
    es[cnt].to = to;
    head[from] = cnt;
}
int dp[100005];
int weight[100005];
int solve(int i){
    if(dp[i] == INT_MIN) return dp[i];
    if(head[i] == 0) {
        dp[i] = weight[i];
        ans = max(ans, weight[i]);
        }
    else{
        int tmp;
        int mn = INT_MAX;
        int mx = INT_MIN;
        int cc = 0;
        for(int j = head[i]; j != 0; j=es[j].next){//利用cc记录分支的个数,如果cc为1,则路径中没有mn
            cc++;
            tmp = solve(es[j].to);
            mn = min(mn,tmp);
            mx = max(mx,tmp);
        }
        dp[i] = max(dp[i],weight[i] + mx);//记录经过i节点的最大分支。
        ans = max(ans, max(weight[i],max(mx+weight[i],max(mx+(cc<=1?0:mn)+weight[i],mx))));//寻求最大路径和
    }
    return dp[i];
}
int main(){
    int n;
    cin>>n;
    for(int i = 1; i <= n; ++i){
        cin>>weight[i];
    }
    int root;
    memset(dp,INT_MIN,sizeof(dp));
    for(int i = 1; i <= n; ++i){
        int tmp;
        cin>>tmp;
        if (tmp == 0) root = i;
        else{
            add_edge(tmp,i);
        }
    }
    solve(root);
    cout<<ans<<endl;
    return 0;
}