D. The Fair Nut and the Best Path
题意
给你一棵树,在树中找出一条路径(也可以只有一个点),
这条路径(点权和-边权和)最大。
做法
我们设dp[i]为以i为出发点向子树方向的最优路径,
那么我们可以很轻松的用儿子的dp数组更新父亲的dp数组
也就是取一个最大的dp[i]-w[v],w[v]表示父亲与这个儿子之间的路径权值
我们有了dp数组,我们发现,对于某个点,如果答案在这个点的子树内而且经过这个点,答案要么是从这个点出发的一条向子树的路径,要么是从某个子树到这个点再到另一颗子树。对于第一种情况,答案很显然就是dp[i]中的最大值,第二种情况,就对每个点在他的儿子中选两个最优的连接起来,同样取所有点的最大值就可以。
比如上图,经过rt的而且在他的子树内的最优路径,一定是在所有的DP[i]?wiDP[i]-w_iDP[i]?wi?中选出两个最大而且大于0的值,连接起来。就这样不断从叶子向上更新就可以得到答案。
代码
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<string.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+5;
vector<int> G[maxn];
vector<ll> val[maxn];
ll w[maxn];
ll dp[maxn];
int n,u,v;
ll we,ans;
vector<ll> tmp;
bool cmp(ll a,ll b)
{
return a>b;
}
void dfs(int rt,int fa)
{
dp[rt]=w[rt];for(int i=0;i<G[rt].size();i++){
int to=G[rt][i];ll va=val[rt][i];if(to==fa) continue ;dfs(to,rt);dp[rt]=max(dp[rt],w[rt]+dp[to]-va);}ans=max(ans,dp[rt]);tmp.clear();for(int i=0;i<G[rt].size();i++){
int to=G[rt][i];ll va=val[rt][i];if(to==fa) continue ;tmp.push_back(dp[to]-va);}sort(tmp.begin(),tmp.end(),cmp);ll tt=w[rt];if(tmp.size()>=2){
if(tmp[0]>=0) tt+=tmp[0];if(tmp[1]>=0) tt+=tmp[1];}ans=max(ans,tt);
}
int main()
{
scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%lld",&w[i]);for(int i=1;i<=n-1;i++){
scanf("%d%d%lld",&u,&v,&we);G[u].push_back(v);G[v].push_back(u);val[u].push_back(we);val[v].push_back(we);}dfs(1,-1);printf("%lld\n",ans);return 0;
}