Count the Number of Inversions
HardPrompt
You are given an integer n and a 2D array requirements, where requirements[i] = [endi, cnti] represents the end index and the inversion count of each requirement.
A pair of indices (i, j) from an integer array nums is called an inversion if:
i < jandnums[i] > nums[j]
Return the number of permutations perm of [0, 1, 2, ..., n - 1] such that for all requirements[i], perm[0..endi] has exactly cnti inversions.
Since the answer may be very large, return it modulo 109 + 7.
Example 1:
Input: n = 3, requirements = [[2,2],[0,0]]
Output: 2
Explanation:
The two permutations are:
[2, 0, 1]- Prefix
[2, 0, 1]has inversions(0, 1)and(0, 2). - Prefix
[2]has 0 inversions.
- Prefix
[1, 2, 0]- Prefix
[1, 2, 0]has inversions(0, 2)and(1, 2). - Prefix
[1]has 0 inversions.
- Prefix
Example 2:
Input: n = 3, requirements = [[2,2],[1,1],[0,0]]
Output: 1
Explanation:
The only satisfying permutation is [2, 0, 1]:
- Prefix
[2, 0, 1]has inversions(0, 1)and(0, 2). - Prefix
[2, 0]has an inversion(0, 1). - Prefix
[2]has 0 inversions.
Example 3:
Input: n = 2, requirements = [[0,0],[1,0]]
Output: 1
Explanation:
The only satisfying permutation is [0, 1]:
- Prefix
[0]has 0 inversions. - Prefix
[0, 1]has an inversion(0, 1).
Constraints:
2 <= n <= 3001 <= requirements.length <= nrequirements[i] = [endi, cnti]0 <= endi <= n - 10 <= cnti <= 400- The input is generated such that there is at least one
isuch thatendi == n - 1. - The input is generated such that all
endiare unique.
Approaches
2 approaches with complexity analysis and trade-offs.
This problem can be solved efficiently using dynamic programming. The core idea is to build the permutations incrementally by length, from 1 to n. At each step i, we calculate the number of permutations of length i for each possible number of inversions j. The key is the recurrence relation that connects the number of permutations of length i with j inversions to the counts for permutations of length i-1.
Algorithm
- Pre-process the
requirementsarray into a map for efficient lookups, mappingend_i + 1(prefix length) tocnt_i. - Determine the maximum required inversion count,
max_cnt, from the requirements. This will be the size of our DP array. - Initialize a DP array,
dp, of sizemax_cnt + 1.dp[j]will store the number of valid permutations of the current length withjinversions. Start withdp[0] = 1representing one empty permutation with 0 inversions. - Iterate
ifrom 1 ton. In each iterationi, we calculate the DP table for permutations of lengthibased on the table for lengthi-1. - Inside the loop, first compute a prefix sum array
psfor the currentdparray.ps[j] = (ps[j-1] + dp[j]) % MOD. - Create a
new_dparray. For eachjfrom 0 tomax_cnt, calculatenew_dp[j]using the recurrencedp[i][j] = sum_{k=0}^{i-1} dp[i-1][j-k]. This sum is efficiently found using the prefix sum array:ps[j] - ps[j-i](with modulo arithmetic). - After computing
new_dp, updatedp = new_dp. - Check if there is a requirement for length
i. IfreqMapcontainsi, let the required count bec. Enforce the constraint by settingdp[j] = 0for allj != c. - After the loop finishes (i.e.,
i=n), the answer is the value indpat the index corresponding to the final requirement's count,dp[reqMap.get(n)].
Walkthrough
Let dp[i][j] be the number of permutations of [0, 1, ..., i-1] with exactly j inversions. A permutation of length i is formed by taking a permutation of [0, ..., i-2] and inserting the number i-1. Since i-1 is the largest number, inserting it at the k-th position from the end adds k new inversions (0 <= k < i). This leads to the recurrence: dp[i][j] = sum_{k=0}^{i-1} dp[i-1][j-k]. This sum can be optimized using prefix sums.
We can use a 1D DP array and iterate from length 1 to n, updating the counts at each step. After each step i, we apply the constraint from requirements for prefixes of length i, if any, by zeroing out counts for non-matching inversion numbers.
import java.util.Map;import java.util.HashMap; class Solution { public int numberOfPermutations(int n, int[][] requirements) { int MOD = 1_000_000_007; Map<Integer, Integer> reqMap = new HashMap<>(); int maxCnt = 0; for (int[] req : requirements) { // Use length (end_i + 1) as key reqMap.put(req[0] + 1, req[1]); maxCnt = Math.max(maxCnt, req[1]); } // dp[j] will store the number of permutations of length `i` with `j` inversions. long[] dp = new long[maxCnt + 1]; dp[0] = 1; // Base case: 1 permutation of length 0 ([]) with 0 inversions. for (int i = 1; i <= n; i++) { long[] newDp = new long[maxCnt + 1]; long[] prefixSum = new long[maxCnt + 1]; // Calculate prefix sums for the previous DP table (length i-1) prefixSum[0] = dp[0]; for (int j = 1; j <= maxCnt; j++) { prefixSum[j] = (prefixSum[j - 1] + dp[j]) % MOD; } // Calculate new DP table for length i for (int j = 0; j <= maxCnt; j++) { // Sum of dp[j-k] for k from 0 to i-1 long ways = prefixSum[j]; if (j >= i) { ways = (ways - prefixSum[j - i] + MOD) % MOD; } newDp[j] = ways; } dp = newDp; // Apply requirement for the current length i if (reqMap.containsKey(i)) { int requiredCnt = reqMap.get(i); if (requiredCnt > maxCnt) { // Impossible requirement return 0; } for (int j = 0; j <= maxCnt; j++) { if (j != requiredCnt) { dp[j] = 0; } } } } // The problem guarantees a requirement for end = n-1 (length n). return (int) dp[reqMap.get(n)]; }}Complexity
Time
O(n * max_cnt). The main loop runs `n` times. Inside the loop, calculating prefix sums and the new DP table each take `O(max_cnt)` time. Given `n <= 300` and `max_cnt <= 400`, this is very efficient.
Space
O(n + max_cnt), where `max_cnt` is the maximum inversion count in `requirements`. We use a map for requirements (`O(n)`) and DP/prefix sum arrays of size `O(max_cnt)`.
Trade-offs
Pros
Highly efficient and passes within the time limits for the given constraints.
Systematically builds the solution by solving smaller, overlapping subproblems.
The use of prefix sums optimizes the DP transition from O(i) to O(1) for each state.
Cons
The logic is more complex than a brute-force approach.
Requires understanding of dynamic programming and the specific recurrence for counting inversions in permutations.
Solutions
Solution
class Solution {public int numberOfPermutations(int n, int[][] requirements) { int[] req = new int[n]; Arrays.fill(req, -1); int m = 0; for (var r : requirements) { req[r[0]] = r[1]; m = Math.max(m, r[1]); } if (req[0] > 0) { return 0; } req[0] = 0; final int mod = (int)1 e9 + 7; int[][] f = new int[n][m + 1]; f[0][0] = 1; for (int i = 1; i < n; ++i) { int l = 0, r = m; if (req[i] >= 0) { l = r = req[i]; } for (int j = l; j <= r; ++j) { for (int k = 0; k <= Math.min(i, j); ++k) { f[i][j] = (f[i][j] + f[i - 1][j - k]) % mod; } } } return f[n - 1][req[n - 1]]; }}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.