2681. Power of Heroes
Problem Description
You are given a 0-indexed integer array nums representing the strength of some heroes. Your task is to find the sum of the power of all possible non-empty groups of heroes.
The power of a group of heroes is calculated using a specific formula:
- For a group with heroes at indices 
i₀, i₁, ..., iₖ, the power is defined as:max(nums[i₀], nums[i₁], ..., nums[iₖ])² × min(nums[i₀], nums[i₁], ..., nums[iₖ]) - In other words, you take the maximum strength value in the group, square it, and multiply by the minimum strength value in the group
 
You need to:
- Consider all possible non-empty subsets of the array (not necessarily contiguous)
 - Calculate the power for each subset using the formula above
 - Sum up all these power values
 - Return the sum modulo 
10⁹ + 7(since the result can be very large) 
For example, if you have a group with strengths [2, 3, 5], the power would be: 5² × 2 = 25 × 2 = 50 (where 5 is the maximum and 2 is the minimum).
The challenge involves efficiently computing this sum across all possible subsets, as the naive approach of generating all subsets would be too slow for large arrays.
Intuition
The key insight is that since we're dealing with maximum and minimum values of subsets, the order of elements in the original array doesn't matter - we can sort it first to make our calculations easier.
Once sorted, let's think about how to count contributions efficiently. For any subset, the power formula is max² × min. If we fix the minimum element, we need to consider all possible subsets that include this minimum and potentially larger elements.
Consider a sorted array [a₁, a₂, a₃, ..., aₙ]. If we fix aᵢ as the minimum element of a subset, then:
- The subset can include 
aᵢitself (power =aᵢ² × aᵢ = aᵢ³) - The subset can include 
aᵢandaᵢ₊₁(power =aᵢ₊₁² × aᵢ) - The subset can include 
aᵢandaᵢ₊₂(power =aᵢ₊₂² × aᵢ) - And so on...
 
But wait - we can also have subsets like {aᵢ, aᵢ₊₁, aᵢ₊₂} where the maximum is aᵢ₊₂. How many such subsets are there? For each element aⱼ where j > i, it can be the maximum element in 2^(j-i-1) different subsets (we can choose to include or exclude each element between i and j).
This means when aᵢ is the minimum, the total contribution is:
aᵢ × (aᵢ² + aᵢ₊₁² + 2×aᵢ₊₂² + 4×aᵢ₊₃² + ... + 2^(n-i-1)×aₙ²)
The coefficient doubles each time because each subsequent element can be the maximum in twice as many subsets as the previous one.
To compute this efficiently, we can process the array from right to left, maintaining a running sum p that represents the weighted sum of squares. For each element, we:
- Add its contribution when it's the minimum (
aᵢ × p) - Add its contribution when it's both min and max (
aᵢ³) - Update 
pfor the next iteration:p = 2×p + aᵢ² 
This way, we avoid recalculating the weighted sums repeatedly and achieve a linear time solution after sorting.
Learn more about Math, Dynamic Programming, Prefix Sum and Sorting patterns.
Solution Approach
Based on our intuition, let's implement the solution step by step:
Step 1: Sort the array Since the order doesn't affect the result and we need to work with minimum and maximum values, we first sort the array in ascending order.
Step 2: Initialize variables
mod = 10^9 + 7for the modulo operationans = 0to accumulate the total power sump = 0to maintain the weighted sum of squares for efficient calculation
Step 3: Process elements from right to left
We iterate through the sorted array in reverse order (from largest to smallest). For each element x:
- 
Add the contribution when
xis the minimum element:- When 
xis both min and max (single element subset):x³ - This is computed as: 
ans = (ans + x² × x) % mod 
 - When 
 - 
Add the contribution from larger elements:
- When 
xis minimum and there are larger elements as maximum:x × p - This is computed as: 
ans = (ans + x × p) % mod 
 - When 
 - 
Update
pfor the next iteration:- The formula 
p = 2×p + x²maintains the weighted sum - Each existing term in 
pgets doubled (representing the doubling of subset counts) - We add 
x²for the current element 
 - The formula 
 
Why this works:
When we process element aᵢ as the minimum:
- The value 
palready contains the weighted sum:aᵢ₊₁² + 2×aᵢ₊₂² + 4×aᵢ₊₃² + ... - Multiplying 
aᵢ × pgives us all contributions whereaᵢis minimum and some larger element is maximum - Adding 
aᵢ³accounts for the subset containing onlyaᵢ - Updating 
p = 2×p + aᵢ²prepares it for the next element:- The 
2×ppart doubles all existing coefficients - Adding 
aᵢ²includes the current element for future calculations 
 - The 
 
Time Complexity: O(n log n) due to sorting, where n is the length of the array
Space Complexity: O(1) excluding the space used for sorting
The modulo operation is applied at each step to prevent integer overflow, ensuring the result stays within bounds.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with a small example: nums = [1, 2, 3]
Step 1: Sort the array
Array is already sorted: [1, 2, 3]
Step 2: Initialize variables
mod = 10^9 + 7ans = 0(total power sum)p = 0(weighted sum of squares)
Step 3: Process from right to left
Iteration 1: Process element 3 (index 2)
- Current element 
x = 3 - Add contribution when 3 is minimum (only subset {3}):
ans = ans + 3² × 3 = 0 + 27 = 27
 - Add contribution from larger elements (none exist):
ans = ans + 3 × p = 27 + 3 × 0 = 27
 - Update p for next iteration:
p = 2 × p + 3² = 2 × 0 + 9 = 9
 
Iteration 2: Process element 2 (index 1)
- Current element 
x = 2 - Add contribution when 2 is minimum:
- Single element subset {2}: 
2³ = 8 ans = ans + 2² × 2 = 27 + 8 = 35
 - Single element subset {2}: 
 - Add contribution from larger elements:
- Subset {2, 3} has power 3² × 2 = 18
 - This is captured by: 
ans = ans + 2 × p = 35 + 2 × 9 = 53 
 - Update p for next iteration:
p = 2 × p + 2² = 2 × 9 + 4 = 22
 
Iteration 3: Process element 1 (index 0)
- Current element 
x = 1 - Add contribution when 1 is minimum:
- Single element subset {1}: 
1³ = 1 ans = ans + 1² × 1 = 53 + 1 = 54
 - Single element subset {1}: 
 - Add contribution from larger elements:
- Subset {1, 2}: power = 2² × 1 = 4
 - Subset {1, 3}: power = 3² × 1 = 9
 - Subset {1, 2, 3}: power = 3² × 1 = 9
 - Total from p: 4 + 9 + 9 = 22
 ans = ans + 1 × p = 54 + 1 × 22 = 76
 
Final answer: 76
Let's verify by listing all subsets and their powers:
- {1}: 1² × 1 = 1
 - {2}: 2² × 2 = 8
 - {3}: 3² × 3 = 27
 - {1, 2}: 2² × 1 = 4
 - {1, 3}: 3² × 1 = 9
 - {2, 3}: 3² × 2 = 18
 - {1, 2, 3}: 3² × 1 = 9
 
Total: 1 + 8 + 27 + 4 + 9 + 18 + 9 = 76 ✓
The key insight is how p accumulates the weighted sum of squares. When processing element 2, p = 9 represents that element 3 can be the maximum in exactly one subset where 2 is minimum. When processing element 1, p = 22 represents:
- Element 2 can be max in 1 subset: 2² × 1 = 4
 - Element 3 can be max in 2 subsets ({1,3} and {1,2,3}): 2 × 3² × 1 = 18
 - Total: 4 + 18 = 22
 
Solution Implementation
1class Solution:
2    def sumOfPower(self, nums: List[int]) -> int:
3        MOD = 10**9 + 7
4      
5        # Sort numbers in ascending order
6        nums.sort()
7      
8        # Initialize result and prefix sum tracker
9        total_sum = 0
10        prefix_contribution = 0
11      
12        # Process numbers from largest to smallest
13        for current_num in reversed(nums):
14            # Add contribution where current_num is the maximum
15            # This includes current_num^3 (when it's alone)
16            total_sum = (total_sum + (current_num * current_num % MOD) * current_num) % MOD
17          
18            # Add contribution from subsequences where current_num is minimum
19            # and other elements form the rest of the subsequence
20            total_sum = (total_sum + current_num * prefix_contribution) % MOD
21          
22            # Update prefix contribution for next iteration
23            # Each previous contribution doubles (can include or exclude current)
24            # Plus new contribution from current_num^2
25            prefix_contribution = (prefix_contribution * 2 + current_num * current_num) % MOD
26      
27        return total_sum
281class Solution {
2    public int sumOfPower(int[] nums) {
3        // Define modulo constant for preventing integer overflow
4        final int MOD = 1_000_000_007;
5      
6        // Sort the array in ascending order
7        Arrays.sort(nums);
8      
9        // Initialize variables
10        long totalSum = 0;  // Stores the final answer
11        long prefixContribution = 0;  // Stores accumulated contribution from previous elements
12      
13        // Process elements from largest to smallest
14        for (int i = nums.length - 1; i >= 0; i--) {
15            long currentValue = nums[i];
16          
17            // Add contribution where current element is the maximum
18            // This represents subsequences ending at current element
19            long currentSquared = currentValue * currentValue % MOD;
20            totalSum = (totalSum + currentSquared * currentValue) % MOD;
21          
22            // Add contribution from subsequences where current element is the minimum
23            // and previous elements form the middle part
24            totalSum = (totalSum + currentValue * prefixContribution % MOD) % MOD;
25          
26            // Update prefix contribution for next iteration
27            // Each previous subsequence can either include or exclude current element
28            // Hence multiply by 2, plus new subsequences starting with current element
29            prefixContribution = (prefixContribution * 2 + currentSquared) % MOD;
30        }
31      
32        return (int) totalSum;
33    }
34}
351class Solution {
2public:
3    int sumOfPower(vector<int>& nums) {
4        const int MOD = 1000000007;  // 10^9 + 7 for modular arithmetic
5      
6        // Sort array in descending order
7        sort(nums.rbegin(), nums.rend());
8      
9        long long totalSum = 0;      // Accumulates the final answer
10        long long prefixContribution = 0;  // Tracks contribution from previously processed elements
11      
12        // Process each number from largest to smallest
13        for (long long currentNum : nums) {
14            // Add contribution where current number is the maximum
15            // This adds currentNum^3 (currentNum as both max and min in single-element subsequence)
16            totalSum = (totalSum + (currentNum * currentNum % MOD) * currentNum) % MOD;
17          
18            // Add contribution from subsequences where current number is the minimum
19            // and previous numbers form the maximum
20            totalSum = (totalSum + currentNum * prefixContribution % MOD) % MOD;
21          
22            // Update prefix contribution for next iteration
23            // Each previous subsequence can either include or exclude the current number
24            // So we double the previous contribution and add currentNum^2 for new subsequences
25            prefixContribution = (prefixContribution * 2 + currentNum * currentNum % MOD) % MOD;
26        }
27      
28        return static_cast<int>(totalSum);
29    }
30};
311/**
2 * Calculates the sum of power of all non-empty subsequences of nums
3 * Power is defined as (max element)^2 * (min element)
4 * @param nums - Array of positive integers
5 * @returns Sum of powers modulo 10^9 + 7
6 */
7function sumOfPower(nums: number[]): number {
8    const MOD = 10 ** 9 + 7;
9  
10    // Sort the array in ascending order
11    nums.sort((a, b) => a - b);
12  
13    let totalSum = 0;
14    // Tracks the contribution from previous elements
15    // Represents sum of (min * max^2) for subsequences ending before current element
16    let previousContribution = 0;
17  
18    // Process elements from largest to smallest
19    for (let i = nums.length - 1; i >= 0; --i) {
20        const currentValue = BigInt(nums[i]);
21      
22        // Add contribution when current element is both max and min (single element subsequence)
23        // Power = currentValue^3
24        totalSum = (totalSum + Number((currentValue * currentValue * currentValue) % BigInt(MOD))) % MOD;
25      
26        // Add contribution from subsequences where current element is the minimum
27        // and some larger element (already processed) is the maximum
28        totalSum = (totalSum + Number((currentValue * BigInt(previousContribution)) % BigInt(MOD))) % MOD;
29      
30        // Update previous contribution for next iteration
31        // Each existing subsequence can either include or exclude the current element
32        // So we multiply by 2, then add new subsequences with current as max
33        previousContribution = Number((BigInt(previousContribution) * 2n + currentValue * currentValue) % BigInt(MOD));
34    }
35  
36    return totalSum;
37}
38Time and Space Complexity
The time complexity is O(n × log n), where n is the length of the array. This is dominated by the sorting operation nums.sort(), which takes O(n × log n) time. The subsequent loop iterates through the array once in O(n) time, performing constant-time operations within each iteration.
The space complexity is O(log n). While the code doesn't explicitly create additional data structures proportional to the input size, the sorting algorithm (typically Timsort in Python) requires O(log n) space for its recursive call stack. The variables ans, p, and mod use only O(1) additional space.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Integer Overflow in Intermediate Calculations
The most critical pitfall in this problem is handling integer overflow during intermediate calculations, particularly when computing current_num * current_num * current_num or current_num * prefix_contribution.
The Problem: Even though we're using modulo operations, the intermediate multiplication can overflow before the modulo is applied. For example:
- If 
current_num = 10^5, thencurrent_num^3 = 10^15, which exceeds the typical integer limit - The expression 
(current_num * current_num % MOD) * current_nummight still overflow if not handled carefully 
Solution: Apply modulo operations more frequently to keep intermediate values small:
# Instead of: total_sum = (total_sum + (current_num * current_num % MOD) * current_num) % MOD # Use: squared = (current_num * current_num) % MOD cubed = (squared * current_num) % MOD total_sum = (total_sum + cubed) % MOD
2. Incorrect Order of Operations with Modulo
Another common mistake is applying modulo operations in the wrong order or missing them in certain places.
The Problem: Forgetting to apply modulo to intermediate results can lead to overflow, while applying it incorrectly can produce wrong results.
Solution: Ensure modulo is applied consistently after each arithmetic operation:
class Solution:
    def sumOfPower(self, nums: List[int]) -> int:
        MOD = 10**9 + 7
        nums.sort()
      
        total_sum = 0
        prefix_contribution = 0
      
        for current_num in reversed(nums):
            # Break down calculations to avoid overflow
            curr_squared = (current_num * current_num) % MOD
            curr_cubed = (curr_squared * current_num) % MOD
          
            # Add current element's contribution
            total_sum = (total_sum + curr_cubed) % MOD
          
            # Add prefix contribution
            contribution = (current_num * prefix_contribution) % MOD
            total_sum = (total_sum + contribution) % MOD
          
            # Update prefix for next iteration
            prefix_contribution = (2 * prefix_contribution + curr_squared) % MOD
      
        return total_sum
3. Misunderstanding the Prefix Contribution Update
The formula prefix_contribution = (2 * prefix_contribution + current_num^2) is crucial but easy to get wrong.
The Problem:
- Forgetting to multiply by 2 (which represents the doubling of subset counts)
 - Adding 
current_numinstead ofcurrent_num^2 - Updating prefix_contribution before using it in calculations
 
Solution:
Always update prefix_contribution after using it in the current iteration's calculations, and ensure you're adding the square of the current number.
4. Processing Order Confusion
Processing the array in the wrong direction can lead to incorrect results.
The Problem: The algorithm relies on processing elements from largest to smallest (after sorting) to correctly accumulate the prefix contributions.
Solution:
Always sort first, then process in reverse order using reversed(nums) or iterate with reverse indices.
Given a sorted array of integers and an integer called target, find the element that
equals to the target and return its index. Select the correct code that fills the
___ in the given code snippet.
1def binary_search(arr, target):
2    left, right = 0, len(arr) - 1
3    while left ___ right:
4        mid = (left + right) // 2
5        if arr[mid] == target:
6            return mid
7        if arr[mid] < target:
8            ___ = mid + 1
9        else:
10            ___ = mid - 1
11    return -1
121public static int binarySearch(int[] arr, int target) {
2    int left = 0;
3    int right = arr.length - 1;
4
5    while (left ___ right) {
6        int mid = left + (right - left) / 2;
7        if (arr[mid] == target) return mid;
8        if (arr[mid] < target) {
9            ___ = mid + 1;
10        } else {
11            ___ = mid - 1;
12        }
13    }
14    return -1;
15}
161function binarySearch(arr, target) {
2    let left = 0;
3    let right = arr.length - 1;
4
5    while (left ___ right) {
6        let mid = left + Math.trunc((right - left) / 2);
7        if (arr[mid] == target) return mid;
8        if (arr[mid] < target) {
9            ___ = mid + 1;
10        } else {
11            ___ = mid - 1;
12        }
13    }
14    return -1;
15}
16Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Prefix Sum The prefix sum is an incredibly powerful and straightforward technique Its primary goal is to allow for constant time range sum queries on an array What is Prefix Sum The prefix sum of an array at index i is the sum of all numbers from index 0 to i By
Want a Structured Path to Master System Design Too? Don’t Miss This!