Find the Maximum Sum of Node Values
HardPrompt
There exists an undirected tree with n nodes numbered 0 to n - 1. You are given a 0-indexed 2D integer array edges of length n - 1, where edges[i] = [ui, vi] indicates that there is an edge between nodes ui and vi in the tree. You are also given a positive integer k, and a 0-indexed array of non-negative integers nums of length n, where nums[i] represents the value of the node numbered i.
Alice wants the sum of values of tree nodes to be maximum, for which Alice can perform the following operation any number of times (including zero) on the tree:
- Choose any edge
[u, v]connecting the nodesuandv, and update their values as follows:nums[u] = nums[u] XOR knums[v] = nums[v] XOR k
Return the maximum possible sum of the values Alice can achieve by performing the operation any number of times.
Example 1:
Input: nums = [1,2,1], k = 3, edges = [[0,1],[0,2]]
Output: 6
Explanation: Alice can achieve the maximum sum of 6 using a single operation:
- Choose the edge [0,2]. nums[0] and nums[2] become: 1 XOR 3 = 2, and the array nums becomes: [1,2,1] -> [2,2,2].
The total sum of values is 2 + 2 + 2 = 6.
It can be shown that 6 is the maximum achievable sum of values.Example 2:
Input: nums = [2,3], k = 7, edges = [[0,1]]
Output: 9
Explanation: Alice can achieve the maximum sum of 9 using a single operation:
- Choose the edge [0,1]. nums[0] becomes: 2 XOR 7 = 5 and nums[1] become: 3 XOR 7 = 4, and the array nums becomes: [2,3] -> [5,4].
The total sum of values is 5 + 4 = 9.
It can be shown that 9 is the maximum achievable sum of values.Example 3:
Input: nums = [7,7,7,7,7,7], k = 3, edges = [[0,1],[0,2],[0,3],[0,4],[0,5]]
Output: 42
Explanation: The maximum achievable sum is 42 which can be achieved by Alice performing no operations.
Constraints:
2 <= n == nums.length <= 2 * 1041 <= k <= 1090 <= nums[i] <= 109edges.length == n - 1edges[i].length == 20 <= edges[i][0], edges[i][1] <= n - 1- The input is generated such that
edgesrepresent a valid tree.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach uses dynamic programming on the tree structure. The core idea is that for any subtree, we want to find the maximum possible sum of node values, considering two scenarios: one where an even number of nodes in the subtree are "flipped" (their value is XORed with k), and one where an odd number of nodes are flipped. This information allows a parent node to make decisions by combining results from its children's subtrees.
Algorithm
- First, build an adjacency list representation of the tree from the
edgesarray. - We perform a Depth First Search (DFS) from an arbitrary root (e.g., node 0).
- The DP state for a node
uwill be a pair of values:dp[u] = [max_sum_even, max_sum_odd].max_sum_even: The maximum sum achievable in the subtree rooted atu, given that an even number of nodes in this subtree have their values flipped.max_sum_odd: The maximum sum achievable in the subtree rooted atu, given that an odd number of nodes in this subtree have their values flipped.
- The DFS function, say
dfs(u, parent), will computedp[u]using a post-order traversal. - Base Case (Leaf Node): For a leaf node
u, there are no subtrees to combine. We only consider flippinguitself.- If we don't flip
u(0 flips, which is even), the sum isnums[u]. So,max_sum_even = nums[u]. - If we flip
u(1 flip, which is odd), the sum isnums[u] XOR k. So,max_sum_odd = nums[u] XOR k.
- If we don't flip
- Recursive Step (Internal Node): For an internal node
u, we first initialize its DP values as if it were a leaf:dp[u] = [nums[u], nums[u] XOR k]. Then, we iterate through each childvofu. For each child, we recursively calldfs(v, u)to getdp[v] = [child_even_sum, child_odd_sum]. We then combine this result withu's current accumulated DP values.- Let
current_even_sumandcurrent_odd_sumbe the DP values foruaccumulated so far (fromuitself and previously processed children). - To get a new total even number of flips, we can either combine an even number of flips from
u's part with an even number fromv's subtree, OR an odd number fromu's part with an odd number fromv's subtree. new_even_sum = max(current_even_sum + child_even_sum, current_odd_sum + child_odd_sum)- Similarly, for an odd number of flips:
new_odd_sum = max(current_even_sum + child_odd_sum, current_odd_sum + child_even_sum)- We update
dp[u]with these new values and proceed to the next child.
- Let
- After the DFS completes for the root,
dp[root][0]will hold the answer. This is because for the entire tree, the total number of nodes we are allowed to flip must be even.
Walkthrough
First, we build an adjacency list representation of the tree from the edges array. We then perform a Depth First Search (DFS) from an arbitrary root (e.g., node 0). The DP state for a node u will be a pair of values: dp[u] = [max_sum_even, max_sum_odd], representing the maximum sum in the subtree of u with an even or odd number of flipped nodes, respectively. The DFS function computes this state in a post-order fashion. For a leaf, the state is simply [nums[u], nums[u] XOR k]. For an internal node, it initializes its state similarly and then iteratively combines the results from its children's subtrees. To combine a child's result, we calculate the new max_sum_even by taking the maximum of (current even + child even) and (current odd + child odd). A similar calculation is done for max_sum_odd. After the DFS completes, the answer is the max_sum_even for the root node, as the total number of flips in the entire tree must be even.
class Solution { private List<List<Integer>> adj; private int[] nums; private int k; public long maximumValueSum(int[] nums, int k, int[][] edges) { this.nums = nums; this.k = k; int n = nums.length; this.adj = new ArrayList<>(); for (int i = 0; i < n; i++) { adj.add(new ArrayList<>()); } for (int[] edge : edges) { adj.get(edge[0]).add(edge[1]); adj.get(edge[1]).add(edge[0]); } long[] result = dfs(0, -1); return result[0]; } // Returns a pair [max_sum_even_flips, max_sum_odd_flips] private long[] dfs(int u, int p) { // dp[0]: max sum for subtree with even number of flipped nodes // dp[1]: max sum for subtree with odd number of flipped nodes long[] dp = new long[]{(long)nums[u], (long)(nums[u] ^ k)}; for (int v : adj.get(u)) { if (v == p) { continue; } long[] childDp = dfs(v, u); long currentEven = dp[0]; long currentOdd = dp[1]; // Combine with child's result // To get even flips in total: (even from u + even from v) or (odd from u + odd from v) dp[0] = Math.max(currentEven + childDp[0], currentOdd + childDp[1]); // To get odd flips in total: (even from u + odd from v) or (odd from u + even from v) dp[1] = Math.max(currentEven + childDp[1], currentOdd + childDp[0]); } return dp; }}Complexity
Time
O(N) where N is the number of nodes. We visit each node and edge once during the DFS.
Space
O(N) for the adjacency list and the recursion stack depth in the worst case (for a skewed tree).
Trade-offs
Pros
It's a standard and structured way to solve problems on trees.
It correctly models the constraints of the problem.
Cons
Requires building a graph and performing a traversal, which is more complex than necessary.
Uses O(N) auxiliary space, which is less optimal than the greedy approach.
This approach is based on a key observation about the nature of the operations. Applying an operation on an edge (u, v) is equivalent to flipping the states of u and v. By applying a sequence of operations along a path between two nodes i and j, we can flip the states of just i and j. This means we can flip any pair of nodes. Consequently, we can flip any set of nodes as long as the total number of flipped nodes is even. The problem then simplifies to a greedy choice for each node, with a final correction step if our greedy choices lead to an odd number of flips.
Algorithm
- Initialize
total_sum = 0,count = 0, andmin_abs_diff = Long.MAX_VALUE. - Iterate through each value
numin thenumsarray. - Calculate the flipped value:
flipped_num = num XOR k. - If
flipped_num > num:- Add
flipped_numtototal_sum. - Increment
count.
- Add
- Else:
- Add
numtototal_sum.
- Add
- Calculate the absolute difference
diff = abs(num - flipped_num). - Update
min_abs_diff = min(min_abs_diff, diff). - After the loop, check if
countis even. - If
countis even, returntotal_sum. - If
countis odd, returntotal_sum - min_abs_diff.
Walkthrough
The core insight is that the tree structure guarantees we can flip any two nodes i and j by applying the operation on all edges along the unique path between them. This is because intermediate nodes on the path are part of two operations, so their values are XORed twice, canceling the effect (val XOR k XOR k = val). Only the endpoints i and j are flipped. Since we can flip any pair of nodes, we can achieve any configuration where an even number of nodes are flipped. The problem then becomes: for each node i, we can either keep its value nums[i] or change it to nums[i] XOR k. We should greedily choose the larger of the two values for each node to maximize the sum. We calculate this greedy sum and count how many nodes we 'flipped' (where nums[i] XOR k > nums[i]). If this count is even, our greedy sum is the answer. If the count is odd, our choice is invalid. To fix this, we must make one change: either un-flip a profitably flipped node or flip an unprofitably unflipped node. To minimize the reduction in sum, we should make the change that has the smallest impact. This is equivalent to finding the node i with the minimum abs(nums[i] - (nums[i] XOR k)). We subtract this minimum absolute difference from our greedy sum to get the final answer.
class Solution { public long maximumValueSum(int[] nums, int k, int[][] edges) { long totalSum = 0; int count = 0; long minAbsDiff = Long.MAX_VALUE; for (int num : nums) { long flippedNum = (long)num ^ k; if (flippedNum > num) { totalSum += flippedNum; count++; } else { totalSum += num; } minAbsDiff = Math.min(minAbsDiff, Math.abs((long)num - flippedNum)); } if (count % 2 == 0) { return totalSum; } else { return totalSum - minAbsDiff; } }}Complexity
Time
O(N) for a single pass through the `nums` array.
Space
O(1) as we only use a few variables to store the running sum, count, and minimum difference.
Trade-offs
Pros
Extremely efficient in both time and space.
Simple to understand and implement once the core insight is grasped.
Ignores the
edgesdata entirely, simplifying the logic.
Cons
The core insight that the graph structure allows any even number of nodes to be flipped is not immediately obvious and requires some graph theory reasoning.
Solutions
Solution
public class Solution { public long MaximumValueSum(int[] nums, int k, int[][] edges) { long f0 = 0, f1 = -0x3f3f3f3f; foreach(int x in nums) { long tmp = f0; f0 = Math.Max(f0 + x, f1 + (x ^ k)); f1 = Math.Max(f1 + x, tmp + (x ^ k)); } return f0; }}class Solution { public long maximumValueSum ( int [] nums , int k , int [][] edges ) { long f0 = 0 , f1 = - 0x3f3f3f3f ; for ( int x : nums ) { long tmp = f0 ; f0 = Math . max ( f0 + x , f1 + ( x ^ k )); f1 = Math . max ( f1 + x , tmp + ( x ^ k )); } return f0 ; } }class Solution { public: long long maximumValueSum ( vector < int >& nums , int k , vector < vector < int >>& edges ) { long long totalSum = 0 ; int count = 0 ; int positiveMin = INT_MAX ; int negativeMax = INT_MIN ; for ( int nodeValue : nums ) { int nodeValAfterOperation = nodeValue ^ k ; totalSum += nodeValue ; int netChange = nodeValAfterOperation - nodeValue ; if ( netChange > 0 ) { positiveMin = min ( positiveMin , netChange ); totalSum += netChange ; count += 1 ; } else { negativeMax = max ( negativeMax , netChange ); } } if ( count % 2 == 0 ) { return totalSum ; } return max ( totalSum - positiveMin , totalSum + negativeMax ); } };class Solution : def maximumValueSum ( self , nums : List [ int ], k : int , edges : List [ List [ int ]]) -> int : f0 , f1 = 0 , - inf for x in nums : f0 , f1 = max ( f0 + x , f1 + ( x ^ k )), max ( f1 + x , f0 + ( x ^ k )) return f0Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.