Find the Sum of Subsequence Powers
HardPrompt
You are given an integer array nums of length n, and a positive integer k.
The power of a subsequence is defined as the minimum absolute difference between any two elements in the subsequence.
Return the sum of powers of all subsequences of nums which have length equal to k.
Since the answer may be large, return it modulo 109 + 7.
Example 1:
Input: nums = [1,2,3,4], k = 3
Output: 4
Explanation:
There are 4 subsequences in nums which have length 3: [1,2,3], [1,3,4], [1,2,4], and [2,3,4]. The sum of powers is |2 - 3| + |3 - 4| + |2 - 1| + |3 - 4| = 4.
Example 2:
Input: nums = [2,2], k = 2
Output: 0
Explanation:
The only subsequence in nums which has length 2 is [2,2]. The sum of powers is |2 - 2| = 0.
Example 3:
Input: nums = [4,3,-1], k = 2
Output: 10
Explanation:
There are 3 subsequences in nums which have length 2: [4,3], [4,-1], and [3,-1]. The sum of powers is |4 - 3| + |4 - (-1)| + |3 - (-1)| = 10.
Constraints:
2 <= n == nums.length <= 50-108 <= nums[i] <= 1082 <= k <= n
Approaches
2 approaches with complexity analysis and trade-offs.
This approach uses dynamic programming to directly build the distribution of powers for subsequences of increasing lengths. We define a DP state that stores not just a single value, but a map from each possible power value to the count of subsequences having that power.
Algorithm
- Sort the Input Array: First, sort the
numsarray in non-decreasing order. This simplifies the calculation of differences, as|a - b|becomesb - afora < b. The set of subsequences remains the same, only their element order might change, which doesn't affect the power calculation. - Define DP State: Let
dp[i][j]be a map wheredp[i][j][p]stores the number of subsequences of lengthjthat can be formed using elements fromnums[0...i], withnums[i]being the largest element, and having a power of exactlyp. - Base Case: For subsequences of length 1, the power is undefined. We can consider it to be infinite. So, for each
ifrom 0 ton-1,dp[i][1]will be a map{ infinity: 1 }. - DP Transition: We build the solution for length
jfrom2tok. To computedp[i][j], we considernums[i]as the largest element. The second largest element must benums[p]for somep < i. The remainingj-2elements must be chosen fromnums[0...p-1].- A subsequence of length
jwithnums[i]as the max andnums[p]as the second max is formed by taking a subsequence of lengthj-1ending atnums[p]and appendingnums[i]. - Let
S'be a subsequence of lengthj-1ending atnums[p]. Its power ispower(S'). The new subsequenceS = S' ∪ {nums[i]}haspower(S) = min(power(S'), nums[i] - nums[p]). - We iterate through
pfromj-2toi-1. For eachp, we iterate through the power distribution mapdp[p][j-1]. For each entry(power_old, count)indp[p][j-1], the new power ismin(power_old, nums[i] - nums[p]). We addcountto the corresponding entry indp[i][j]'s map.
- A subsequence of length
- Calculate Total Sum: After filling the
dptable up to lengthk, the total sum of powers is the sum ofpower * countfor all entries in alldp[i][k]maps, forifromk-1ton-1. All calculations should be done modulo10^9 + 7.
Walkthrough
The core idea is to compute, for each length j and ending element nums[i], how many subsequences have a certain power. We can define dp[i][j] as a hash map, where keys are power values and values are the counts of subsequences of length j ending at nums[i] with that power.
To compute dp[i][j], we iterate through all possible previous elements nums[p] (p < i). For each p, we look at the power distributions of subsequences of length j-1 ending at p, which is dp[p][j-1]. For each (old_power, count) pair in dp[p][j-1], we form a new subsequence by adding nums[i]. The new power will be min(old_power, nums[i] - nums[p]). We update the map dp[i][j] with this new information.
After computing these distributions for all i and for j=k, the final answer is obtained by summing up power * count over all these distributions.
import java.util.Arrays;import java.util.HashMap;import java.util.Map; class Solution { public int sumOfPowers(int[] nums, int k) { Arrays.sort(nums); int n = nums.length; long MOD = 1_000_000_007; // dp[i][j] is a map from power to count for subsequences of length j ending at nums[i] Map<Integer, Long>[][] dp = new HashMap[n][k + 1]; for (int i = 0; i < n; i++) { for (int j = 0; j <= k; j++) { dp[i][j] = new HashMap<>(); } } // Base case: subsequences of length 1 for (int i = 0; i < n; i++) { dp[i][1].put(Integer.MAX_VALUE, 1L); // Power is effectively infinite } for (int len = 2; len <= k; len++) { for (int i = len - 1; i < n; i++) { for (int p = len - 2; p < i; p++) { int diff = nums[i] - nums[p]; for (Map.Entry<Integer, Long> entry : dp[p][len - 1].entrySet()) { int oldPower = entry.getKey(); long count = entry.getValue(); int newPower = Math.min(oldPower, diff); dp[i][len].put(newPower, (dp[i][len].getOrDefault(newPower, 0L) + count) % MOD); } } } } long totalSumOfPowers = 0; for (int i = k - 1; i < n; i++) { for (Map.Entry<Integer, Long> entry : dp[i][k].entrySet()) { long power = entry.getKey(); long count = entry.getValue(); totalSumOfPowers = (totalSumOfPowers + (power * count) % MOD) % MOD; } } return (int) totalSumOfPowers; }}Complexity
Time
O(k * n^2 * D), where D is the number of distinct power values. In the worst case, D is O(n^2), leading to a time complexity of O(k * n^4). Given n <= 50, this might be too slow but could pass if the number of distinct powers is small in practice.
Space
O(k * n * D), where D is the number of distinct power values. In the worst case, D can be O(n^2), leading to a space complexity of O(k * n^3).
Trade-offs
Pros
It's a direct and intuitive DP formulation of the problem.
It correctly solves the problem without complex combinatorial identities.
Cons
The time complexity is high, making it potentially slow for the given constraints, though it might just pass.
The space complexity is also significant due to storing maps for each DP state.
A more efficient approach transforms the problem. Instead of summing the powers directly, we sum the counts of subsequences that meet a certain power threshold. The sum of powers of all subsequences S is equal to the sum, over all possible positive integers d, of the number of subsequences with power greater than or equal to d.
The values of d for which this count changes are precisely the differences nums[j] - nums[i]. This allows us to discretize the problem. We only need to calculate the count for each unique difference value.
Algorithm
- Sort the Input Array: Sort
numsin non-decreasing order. - Key Identity: The core of this approach is the identity:
Sum(power(S)) = Sum_{d > 0} (number of subsequences with power >= d). A subsequenceSwithpower(S) = pis counted in the sum ford=1, 2, ..., p, contributing exactlypto the total sum. - Discretize Differences: The number of subsequences with power
>= donly changes whendcrosses a value equal tonums[j] - nums[i]. So, we only need to consider theseO(n^2)difference values. LetVbe the sorted list of unique positive differencesnums[j] - nums[i]. - Rewrite Sum: The total sum can be expressed as
Sum_{i=1 to m} (v_i - v_{i-1}) * N(>=v_i), wherev_iare the unique sorted differences fromV,v_0 = 0, andN(>=v_i)is the number of subsequences of lengthkwith power at leastv_i. - DP for N(>=v): For a fixed difference
v, we can calculateN(>=v)using dynamic programming inO(n*k)time.- Let
dp[i][j]be the number of subsequences of lengthjfromnums[0...i-1]ending withnums[i-1]and having a minimum difference of at leastv. - The transition is
dp[i][j] = sum(dp[p][j-1])for allp < isuch thatnums[i-1] - nums[p-1] >= v. - This summation can be optimized. Let
S[i][j]be the prefix sumsum_{l=1 to i} dp[l][j]. Thendp[i][j] = S[m][j-1], wheremis the largest index such thatnums[m-1] <= nums[i-1] - v. The indexmcan be found efficiently using a two-pointer technique across theiloop.
- Let
- Main Loop: Iterate through the sorted unique differences
vfromV. In each iteration, calculateN(>=v)using theO(n*k)DP. Then add(v - prev_v) * N(>=v)to the total sum. Updateprev_v = vand continue. Remember to use modulo arithmetic.
Walkthrough
First, we sort the nums array. The main insight is to change the summation: Sum(power(S)) = Sum_{d > 0} (count of subsequences with power >= d). Since the count of subsequences with power >= d is a step function that only changes at values d = nums[j] - nums[i], we can compute the sum more efficiently.
Let v_1, v_2, ..., v_m be the unique sorted positive differences. The total sum is (v_1 - v_0)*N(>=v_1) + (v_2 - v_1)*N(>=v_2) + ..., where v_0 = 0 and N(>=v) is the number of subsequences of length k with power at least v.
For each required v, we can compute N(>=v) using an O(n*k) dynamic programming algorithm. Let dp[i][j] be the number of valid subsequences of length j using elements from nums[0...i-1] and ending with nums[i-1]. To form such a subsequence, we must pick a previous element nums[p-1] such that nums[i-1] - nums[p-1] >= v. The number of ways is the sum of dp[p][j-1] over all valid p. This sum can be optimized using prefix sums and a two-pointer approach to achieve O(n*k) complexity for each v.
Since there are O(n^2) unique differences, the total time complexity becomes O(n^2 * n * k) = O(n^3 * k).
import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.HashSet;import java.util.List;import java.util.Set; class Solution { long MOD = 1_000_000_007; public int sumOfPowers(int[] nums, int k) { Arrays.sort(nums); int n = nums.length; Set<Integer> diffSet = new HashSet<>(); for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { diffSet.add(nums[j] - nums[i]); } } List<Integer> diffs = new ArrayList<>(diffSet); Collections.sort(diffs); long totalSum = 0; long prev_v = 0; for (int v : diffs) { long count = countSubsequencesWithMinDiff(nums, k, v); long term = ((long)(v - prev_v) * count) % MOD; totalSum = (totalSum + term) % MOD; prev_v = v; } return (int) totalSum; } private long countSubsequencesWithMinDiff(int[] nums, int k, int minDiff) { int n = nums.length; long[][] dp = new long[n + 1][k + 1]; long[][] prefixSum = new long[n + 1][k + 1]; for (int i = 1; i <= n; i++) { dp[i][1] = 1; prefixSum[i][1] = prefixSum[i - 1][1] + dp[i][1]; } for (int j = 2; j <= k; j++) { int p_ptr = 1; for (int i = 1; i <= n; i++) { while (p_ptr < i && nums[i - 1] - nums[p_ptr - 1] >= minDiff) { p_ptr++; } // p_ptr is now at the first index that doesn't satisfy the condition // So we can sum up to p_ptr - 1 if (p_ptr > 0) { dp[i][j] = prefixSum[p_ptr - 1][j - 1]; } } for (int i = 1; i <= n; i++) { prefixSum[i][j] = (prefixSum[i - 1][j] + dp[i][j]) % MOD; } } return prefixSum[n][k]; }}Complexity
Time
O(n^2 * log(n^2) + D * n * k), where D is the number of unique differences. D can be up to O(n^2). The dominant part is `D * n * k`, leading to a total complexity of O(n^3 * k).
Space
O(n^2 + n*k). O(n^2) to store the unique differences and O(n*k) for the DP table in the helper function.
Trade-offs
Pros
Significantly more efficient than the direct DP approach.
The complexity is well within the limits for the given constraints.
Cons
The logic is more complex, relying on a combinatorial identity and a DP on top of it.
Implementation requires careful handling of several nested loops and data structures.
Solutions
Solution
class Solution {private Map<Long, Integer> f = new HashMap<>();private final int mod = (int)1 e9 + 7;private int[] nums;public int sumOfPowers(int[] nums, int k) { Arrays.sort(nums); this.nums = nums; return dfs(0, nums.length, k, Integer.MAX_VALUE); }private int dfs(int i, int j, int k, int mi) { if (i >= nums.length) { return k == 0 ? mi : 0; } long key = (1L * mi) << 18 | (i << 12) | (j << 6) | k; if (f.containsKey(key)) { return f.get(key); } int ans = dfs(i + 1, j, k, mi); if (j == nums.length) { ans += dfs(i + 1, i, k - 1, mi); } else { ans += dfs(i + 1, i, k - 1, Math.min(mi, nums[i] - nums[j])); } ans %= mod; f.put(key, ans); return ans; }}class Solution: def sumOfPowers(self, nums: List[int], k: int) -> int: @ cache def dfs(i: int, j: int, k: int, mi: int) -> int: if i >= n: return mi if k == 0 else 0 ans = dfs(i + 1, j, k, mi) if j == n: ans += dfs(i + 1, i, k - 1, mi) else: ans += dfs(i + 1, i, k - 1, min(mi, nums[i] - nums[j])) ans %= mod return ans mod = 10 ** 9 + 7 n = len(nums) nums . sort() return dfs(0, n, k, inf)class Solution {public: int sumOfPowers(vector<int> &nums, int k) { unordered_map<long long, int> f; const int mod = 1e9 + 7; int n = nums.size(); sort(nums.begin(), nums.end()); function<int(int, int, int, int)> dfs = [&](int i, int j, int k, int mi) { if (i >= n) { return k == 0 ? mi : 0; } long long key = (1LL * mi) << 18 | (i << 12) | (j << 6) | k; if (f.contains(key)) { return f[key]; } long long ans = dfs(i + 1, j, k, mi); if (j == n) { ans += dfs(i + 1, i, k - 1, mi); } else { ans += dfs(i + 1, i, k - 1, min(mi, nums[i] - nums[j])); } ans %= mod; f[key] = ans; return f[key]; }; return dfs(0, n, k, INT_MAX); }};Video 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.