做法:题目要求选出两个节点,满足任意一个不是另一个的祖先节点,最大化以两个节点为根的子树的点权和。
满足任意一个不是另一个的祖先节点说明对于一个节点u来说,这两个节点必须是u的子孙节点,且处于不同的子树,我们可以在dfs的时候,传递上来更新,同时,必须是至少两个,我们可以把遍历子树的操作看成一个简单的循环,只有当大于等于第二次循环的时候,才更新答案。我们用一个\(mx[u]\)表示以u为根的子树中,最大的子树和。
这道题和很多统计子树的题目很像,考虑了子树对子树间的影响。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 200005;
const ll inf = 1e18;
ll ans = -inf;
int h[N], e[N * 2], ne[N * 2], idx;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int w[N], n;
ll mx[N];
ll dfs(int u, int fa)
{
ll sum = w[u];
mx[u] = -inf;
for(int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if(j == fa) continue;
sum += dfs(j, u);
//有多余两棵子树,才更新ans
if(mx[u] != -inf) ans = max(ans, mx[u] + mx[j]);
mx[u] = max(mx[u], mx[j]);
}
mx[u] = max(mx[u], sum);
return sum;
}
int main()
{
memset(h, -1, sizeof h);
//int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%d", &w[i]);
int u, v;
for(int i = 1; i < n; ++i)
{
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
dfs(1, 0);
if(ans == -inf) printf("Error");
else printf("%lld\n", ans);
return 0;
}