P12710 [KOI 2021 Round 1] 两个团队
解题思路
问题分析
本题要求在一棵有根树中选择两个团队负责人,每个负责人及其团队需满足特定条件,目标是最大化两个团队的得分总和。
关键观察:
-
团队性质:对于以节点x为负责人的团队,必须包含x及其所有下属中必要的节点(即如果包含某个节点,则必须包含其直属上司,除了负责人自己)。
-
团队得分:实际上,以x为负责人的团队得分就是包含x的子树中,按照规则必须选择的一些节点的能力值之和。这可以通过动态规划来计算。
-
动态规划状态:
-
f[x]:表示以x为根的子树中,选择以x为负责人的团队所能得到的最大得分(即包含x的连通子图的最大权值和)。 -
max_k[x]:表示以x为根的子树中,所有团队得分的最大值(即所有f[y]for y in subtree(x)的最大值)。 -
g[x]:表示以x为根的子树中,去掉以x为负责人的团队后,剩余部分中能取到的最大团队得分(即不与x团队重叠的最大团队得分)。
-
算法步骤:
-
DFS遍历:从根节点开始DFS,递归计算每个节点的
f[x]、max_k[x]和g[x]。 -
状态转移:
-
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的子树)。
-
-
更新答案:在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; }

浙公网安备 33010602011771号