最大节点价值之和
给你一棵 `n` 个节点的 无向 树,节点从 `0` 到 `n - 1` 编号。树以长度为 `n - 1` 下标从 0 开始的二维整数数组 `edges` 的形式给你,其中 `edges[i] = [ui, vi]` 表示树中节点 `ui` 和 `vi` 之间有一条边。同时给你一个 正 整数 `k` 和一个长度为 `n` 下标从 0 开始的 非负 整数数组 `nums` ,其中 `nums[i]` 表示节点 `i` 的 价值 。
Alice 想 最大化 树中所有节点价值之和。为了实现这一目标,Alice 可以执行以下操作 任意 次(包括 0 次):
- 选择连接节点 `u` 和 `v` 的边 `[u, v]` ,并将它们的值更新为:
- `nums[u] = nums[u] XOR k`
- `nums[v] = nums[v] XOR k`
请你返回 Alice 通过执行以上操作 任意次 后,可以得到所有节点 价值之和 的 最大值 。
示例 1:
输入:nums = [1,2,1], k = 3, edges = [[0,1],[0,2]] 输出:6 解释:Alice 可以通过一次操作得到最大价值和 6 : - 选择边 [0,2] 。nums[0] 和 nums[2] 都变为:1 XOR 3 = 2 ,数组 nums 变为:[1,2,1] -> [2,2,2] 。 所有节点价值之和为 2 + 2 + 2 = 6 。 6 是可以得到最大的价值之和。
示例 2:
输入:nums = [2,3], k = 7, edges = [[0,1]] 输出:9 解释:Alice 可以通过一次操作得到最大和 9 : - 选择边 [0,1] 。nums[0] 变为:2 XOR 7 = 5 ,nums[1] 变为:3 XOR 7 = 4 ,数组 nums 变为:[2,3] -> [5,4] 。 所有节点价值之和为 5 + 4 = 9 。 9 是可以得到最大的价值之和。
示例 3:
输入:nums = [7,7,7,7,7,7], k = 3, edges = [[0,1],[0,2],[0,3],[0,4],[0,5]] 输出:42 解释:Alice 不需要执行任何操作,就可以得到最大价值之和 42 。
提示:
- `2 4`
- `1 9`
- `0 9`
- `edges.length == n - 1`
- `edges[i].length == 2`
- `0
参考解法如下:
python
from typing import List
class Solution:
def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int:
"""
LeetCode 3068. 最大节点价值之和
核心结论:
在一棵连通树上,每次操作会翻转一条边的两个端点。
因此最终被 XOR k 的节点个数一定是偶数;
反过来,任意偶数个节点也都可以通过若干条树边操作实现。
所以任务变成:
对每个节点,在 nums[i] 和 nums[i] XOR k 中选一个,
但选择 XOR k 的节点数量必须为偶数,最大化总和。
"""
total = 0
changed_count = 0
min_abs_diff = float("inf")
for x in nums:
y = x ^ k
diff = y - x
# 先贪心地选更大的值
total += max(x, y)
# 统计有正收益的 XOR 节点个数
if diff > 0:
changed_count += 1
# 如果最后正收益节点数量为奇数,
# 需要牺牲一个最小代价来调整奇偶性
min_abs_diff = min(min_abs_diff, abs(diff))
# XOR 节点数必须为偶数
if changed_count % 2 == 0:
return total
return total - min_abs_diff关键思想是把树上的边操作转化为“最终哪些点被 XOR k”。一次操作会同时改变两个端点,所以被改变的节点个数的奇偶性一定不变,即最终被 XOR k 的节点数必须是偶数。由于给定图是一棵连通树,对任意两个节点,可以沿着它们之间的路径依次操作路径上的边,路径中间节点会被 XOR 两次而抵消,最终只会让这两个端点各 XOR 一次。因此任意一对节点都可以一起被改变,进一步可以推出任意偶数个节点都可以被改变。于是 edges 不需要真正建图,只用到“它是一棵连通树”这个性质。接下来定义收益 diff = (nums[i] XOR k) - nums[i]。如果 diff > 0,单独看这个节点当然希望选择 XOR 后的值;如果 diff <= 0,则希望保留原值。先贪心累加每个节点的较大值,并统计正收益节点数量 changed_count。如果 changed_count 是偶数,直接合法;如果是奇数,就必须调整一个节点的选择:要么放弃一个正收益节点,要么额外选择一个非正收益节点,本质损失都是 abs(diff),因此减去全局最小的 abs(diff) 即可。复杂度 O(n),额外空间 O(1)。边界条件包括:所有 diff 都非正时答案通常是原数组和;正收益节点数为奇数时必须扣除最小损失;存在 diff = 0 时可以用它无代价调整奇偶性;n = 2 时仍然成立;nums[i] 和 k 可达 1e9,总和可能超过 32 位整数,Python 无需额外处理,其他语言要用 long long。常见错误包括:误以为每个节点可以独立选择,忽略偶数约束;只考虑删除最小正收益,忘记也可以加入一个负收益节点;对 diff = 0 的奇偶调整处理错误;花大量代码做树形 DP 但没有抓住连通树的线性代数性质;错误地模拟操作导致复杂度和正确性都不可控。考察中高质量答案应能清楚证明“可达状态等价于任意偶数个节点被 XOR k”,并给出奇数正收益时为什么扣 min(abs(diff)) 的理由。
- 为什么任意偶数个节点都可以通过树边操作实现
- 如果给定的不是树而是非连通图,条件会如何变化
- 能否用动态规划写出等价解法并说明和贪心的关系