P12710 [KOI 2021 Round 1] 两个团队

解题思路

问题分析

本题要求在一棵有根树中选择两个团队负责人,每个负责人及其团队需满足特定条件,目标是最大化两个团队的得分总和。

关键观察:

  1. 团队性质:对于以节点x为负责人的团队,必须包含x及其所有下属中必要的节点(即如果包含某个节点,则必须包含其直属上司,除了负责人自己)。

  2. 团队得分:实际上,以x为负责人的团队得分就是包含x的子树中,按照规则必须选择的一些节点的能力值之和。这可以通过动态规划来计算。

  3. 动态规划状态:

    • f[x]:表示以x为根的子树中,选择以x为负责人的团队所能得到的最大得分(即包含x的连通子图的最大权值和)。

    • max_k[x]:表示以x为根的子树中,所有团队得分的最大值(即所有f[y] for y in subtree(x)的最大值)。

    • g[x]:表示以x为根的子树中,去掉以x为负责人的团队后,剩余部分中能取到的最大团队得分(即不与x团队重叠的最大团队得分)。

算法步骤:

  1. DFS遍历:从根节点开始DFS,递归计算每个节点的f[x]max_k[x]g[x]

  2. 状态转移:

    • f[x]初始为x自身的能力值。对于每个子节点v,如果f[v] > 0,则加入x的团队(因为能增加总分),否则不加入。

    • max_k[x]取所有子节点的max_k[v]f[x]中的最大值。

    • g[x]表示在x的子树中,除去x的团队后,能得到的最大团队得分。这有两种情况:
      a. 该团队在某个子节点v的子树中,且与x的团队不重叠(即通过g[v]得到)。
      b. 该团队是某个子节点v的团队(即max_k[v]),但x的团队没有包含v(因为f[v] <= 0,所以x的团队没有包含v的子树)。

  3. 更新答案:在DFS过程中,尝试用两种方式更新答案:

    • g[x] + f[x]:即x的团队得分加上在x的子树中另一个不与x团队重叠的团队得分。

    • 两个最大的max_k[v]之和:即在x的两个不同子树中的两个团队得分之和(因为它们互不重叠)。

代码注释

#include<bits/stdc++.h>
#define int long long  // 定义int为long long类型,防止溢出
using namespace std;
const int inf=100000000000000000;  // 定义一个极大的负数,表示无穷小
int w[200005],max_k[200005],f[200005],g[200005],ans=-inf;  // w存储能力值,max_k存储子树中最大团队得分,f存储以当前节点为负责人的团队得分,g存储子树中不与当前团队重叠的最大团队得分,ans存储最终答案
vector<int>G[200005];  // 存储树的邻接表

void dfs(int x)
{
    int maxx=-inf,cmax=-inf;  // maxx和cmax用于记录子节点中最大的两个max_k值
    g[x]=max_k[x]=-inf;  // 初始化g[x]和max_k[x]为无穷小
    f[x]=w[x];  // f[x]初始为节点x自身的能力值
    for(auto v:G[x])  // 遍历x的所有子节点
    {
        dfs(v);  // 递归处理子节点
        if(f[v]>0)  // 如果子节点v的团队得分为正,则加入x的团队
        {
            f[x]+=f[v];  // 将v的团队得分加入x的团队
            g[x]=max(g[x],g[v]);  // 更新g[x]为子节点g[v]的最大值(因为v的团队已被包含,所以只能考虑g[v])
        }
        else  // 如果v的团队得分为负,则不加入x的团队
        {
            g[x]=max(g[x],max_k[v]);  // 此时可以直接用v的子树中的最大团队得分更新g[x]
        }
        max_k[x]=max(max_k[x],max_k[v]);  // 用子节点的max_k更新当前节点的max_k
        // 维护最大的两个max_k值
        if(max_k[v]>=maxx)  // 如果当前子节点的max_k大于等于最大值
        {
            cmax=maxx;  // 更新次大值
            maxx=max_k[v];  // 更新最大值
        }
        else if(max_k[v]>=cmax)  // 如果大于等于次大值
        {
            cmax=max_k[v];  // 更新次大值
        }
    }
    max_k[x]=max(max_k[x],f[x]);  // 用f[x]更新max_k[x]
    // 更新答案:两种可能
    // 1. x的团队得分f[x]加上子树中另一个不重叠的团队得分g[x]
    // 2. 两个不同子树中的最大团队得分之和(maxx+cmax)
    ans=max(ans,max(g[x]+f[x],maxx+cmax));
}

signed main()
{
    int n,fa;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>w[i]>>fa;  // 读入每个节点的能力值和上司编号
        if(i!=1) G[fa].push_back(i);  // 如果不是根节点,则添加到上司的子节点列表中
    }
    dfs(1);  // 从根节点开始DFS
    cout<<ans;  // 输出答案
    return 0;
}

 

posted @ 2025-08-29 15:45  CRt0729  阅读(9)  评论(0)    收藏  举报