在X森林里,上帝创建了生命之树。
他给每棵树的每个节点(叶子也称为一个节点)上,都标了一个整数,代表这个点的和谐值。
上帝要在这棵树内选出一个非空节点集 S,使得对于 S 中的任意两个点 a,b,都存在一个点列 {a,v1,v2,…,vk,b} 使得这个点列中的每个点都是 S 里面的元素,且序列中相邻两个点间有一条边相连。
在这个前提下,上帝要使得 S 中的点所对应的整数的和尽量大。
这个最大的和就是上帝给生命之树的评分。
经过 atm 的努力,他已经知道了上帝给每棵树上每个节点上的整数。
但是由于 atm 不擅长计算,他不知道怎样有效的求评分。
他需要你为他写一个程序来计算一棵树的分数。
输入格式
第一行一个整数 n 表示这棵树有 n 个节点。
第二行 n 个整数,依次表示每个节点的评分。
接下来 n−1 行,每行 2 个整数 u,v,表示存在一条 u 到 v 的边。
由于这是一棵树,所以是不存在环的。
树的节点编号从 1 到 n。
输出格式
输出一行一个数,表示上帝给这棵树的分数。
数据范围
1≤n≤105,
每个节点的评分的绝对值均不超过 106。
输入样例:
5
1 -2 -3 4 5
4 2
3 1
1 2
2 5
输出样例:
8
树形DP。设dp[x]为以x为根的子树里包含x的连通块的最大分数,有转移方程(dp[x] = c[x] + Sigma max(dp[y], 0))
最后答案对所有dp[i]取max即可。
代码瞎写的==写麻烦了
#include <iostream>
#define int long long
#define N 100005
using namespace std;
int n, tot = 0, head[N], ver[2 * N], Next[2 * N];
int c[N];
long long dp[N] = {0}, dp1[N] = {0}; //dp[i]表示以i为根的子树里包含的子联通块的分数最大值 有可能包含i这个点 也可能在i的子树里
bool in[N] = {0};//in[i] = 1 表示i这个点在i为根的子树里包含的最大分数的联通块内
void add(int x, int y)
{
ver[++tot] = y, Next[tot] = head[x], head[x] = tot;
}
long long ans = -1e14;
void dfs(int x, int pre)
{
long long now = -1e14;
for (int i = head[x]; i; i = Next[i])
{
int y = ver[i];
if(y == pre)
continue;
dfs(y, x);
now = max(now, dp[y]);
}
dp[x] = now;
dp1[x] = c[x];
if (c[x] >= now)
{
dp[x] = c[x];
in[x] = 1;
}
long long tmp = c[x];
for (int i = head[x]; i; i = Next[i])
{
int y = ver[i];
if(in[y] && dp[y] >= 0)
{
tmp += 1ll * dp[y];
}
if(dp1[y] >= 0)
dp1[x] += dp1[y];
}
if(tmp >= now)
{
dp[x] = tmp;
in[x] = 1;
}
ans = max(ans, dp[x]);
ans = max(ans, dp1[x]);
}
signed main()
{
cin >> n;
for (int i = 1; i <= n; i++) cin >> c[i];
for (int i = 1; i <= n - 1; i++)
{
int u, v;
cin >> u >> v;
add(u, v);
add(v, u);
}
dfs(1, 0);
cout << ans;
return 0;
}