题目链接

做法:题目要求选出两个节点,满足任意一个不是另一个的祖先节点,最大化以两个节点为根的子树的点权和。
满足任意一个不是另一个的祖先节点说明对于一个节点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;
}