• 牛客练习赛78 C.CCA的子树(树形DP)


    题目链接

    做法:题目要求选出两个节点,满足任意一个不是另一个的祖先节点,最大化以两个节点为根的子树的点权和。
    满足任意一个不是另一个的祖先节点说明对于一个节点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
    ", ans);
        
        return 0;
    }
    
  • 相关阅读:
    三维偏序(陌上花开)
    【题解】洛谷P4735最大异或和
    【题解】CF#229 E-Gifts
    【题解】JXOI2017颜色
    【题解】SDOI2009Bill的挑战
    【题解】CF#960 H-Santa's Gift
    【题解】Atcoder ARC#85 E-MUL
    【题解】CF#1012 C-Hill
    【题解】CF#852 E-Casinos and travel
    【题解】Atcoder ARC#90 F-Number of Digits
  • 原文地址:https://www.cnblogs.com/pixel-Teee/p/14527425.html
Copyright © 2020-2023  润新知