Count the Number of Inversions
HardUpdated: Aug 2, 2025
Practice on:
Problem
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 10^9 + 7.
Examples
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.
- `[1, 2, 0]`
- Prefix `[1, 2, 0]` has inversions `(0, 2)` and `(1, 2)`.
- Prefix `[1]` has 0 inversions.
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.
Solution
Method 1 – Dynamic Programming with Prefix Constraints
Intuition
We want to count the number of permutations of [0, 1, ..., n-1] such that for each prefix ending at endi, the number of inversions is exactly cnti. This is a classic DP on permutations with inversion count, but with additional prefix constraints.
Approach
- Let
dp[i][k]be the number of ways to arrange the firstinumbers with exactlykinversions. - For each prefix length
i, if there is a requirement for that prefix, only keep the DP for the required inversion count. - For each
ifrom 1 to n, updatedp[i][k]by adding all possible ways to insert the new number at positionj(which addsjnew inversions). - Use prefix sums to optimize the DP transitions.
- The answer is the sum of all valid DP states for the full array that satisfy all requirements.
Code
C++
class Solution {
public:
int countInversions(int n, vector<vector<int>>& requirements) {
const int MOD = 1e9+7;
vector<int> req(n, -1);
for (auto& r : requirements) req[r[0]] = r[1];
vector<vector<int>> dp(n+1, vector<int>(n*(n-1)/2+1));
dp[0][0] = 1;
for (int i = 1; i <= n; ++i) {
vector<int> pre(n*(n-1)/2+2);
for (int k = 0; k <= (i-1)*(i-2)/2; ++k) pre[k+1] = (pre[k] + dp[i-1][k]) % MOD;
for (int k = 0; k <= i*(i-1)/2; ++k) {
int l = max(0, k-(i-1)), r = min(k, (i-1)*(i-2)/2);
if (l > r) continue;
dp[i][k] = (pre[r+1] - pre[l] + MOD) % MOD;
}
if (req[i-1] != -1) {
vector<int> ndp(n*(n-1)/2+1);
ndp[req[i-1]] = dp[i][req[i-1]];
dp[i] = ndp;
}
}
int ans = 0;
for (int k = 0; k <= n*(n-1)/2; ++k) ans = (ans + dp[n][k]) % MOD;
return ans;
}
};
Python
class Solution:
def countInversions(self, n: int, requirements: list[list[int]]) -> int:
MOD = 10**9+7
req = [-1]*n
for endi, cnti in requirements:
req[endi] = cnti
max_inv = n*(n-1)//2
dp = [0]*(max_inv+1)
dp[0] = 1
for i in range(1, n+1):
ndp = [0]*(max_inv+1)
pre = [0]*(max_inv+2)
for k in range((i-1)*(i-2)//2+1):
pre[k+1] = (pre[k] + dp[k]) % MOD
for k in range(i*(i-1)//2+1):
l = max(0, k-(i-1))
r = min(k, (i-1)*(i-2)//2)
if l > r: continue
ndp[k] = (pre[r+1] - pre[l]) % MOD
if req[i-1] != -1:
tmp = [0]*(max_inv+1)
tmp[req[i-1]] = ndp[req[i-1]]
ndp = tmp
dp = ndp
return sum(dp) % MOD
Java
class Solution {
public int countInversions(int n, int[][] requirements) {
int MOD = 1_000_000_007;
int[] req = new int[n];
Arrays.fill(req, -1);
for (int[] r : requirements) req[r[0]] = r[1];
int maxInv = n*(n-1)/2;
int[] dp = new int[maxInv+1];
dp[0] = 1;
for (int i = 1; i <= n; ++i) {
int[] ndp = new int[maxInv+1];
int[] pre = new int[maxInv+2];
for (int k = 0; k <= (i-1)*(i-2)/2; ++k) pre[k+1] = (pre[k] + dp[k]) % MOD;
for (int k = 0; k <= i*(i-1)/2; ++k) {
int l = Math.max(0, k-(i-1)), r = Math.min(k, (i-1)*(i-2)/2);
if (l > r) continue;
ndp[k] = (pre[r+1] - pre[l] + MOD) % MOD;
}
if (req[i-1] != -1) {
int[] tmp = new int[maxInv+1];
tmp[req[i-1]] = ndp[req[i-1]];
ndp = tmp;
}
dp = ndp;
}
int ans = 0;
for (int v : dp) ans = (ans + v) % MOD;
return ans;
}
}
Kotlin
class Solution {
fun countInversions(n: Int, requirements: Array<IntArray>): Int {
val MOD = 1_000_000_007
val req = IntArray(n) { -1 }
for ((endi, cnti) in requirements) req[endi] = cnti
val maxInv = n*(n-1)/2
var dp = IntArray(maxInv+1)
dp[0] = 1
for (i in 1..n) {
val ndp = IntArray(maxInv+1)
val pre = IntArray(maxInv+2)
for (k in 0..(i-1)*(i-2)/2) pre[k+1] = (pre[k] + dp[k]) % MOD
for (k in 0..i*(i-1)/2) {
val l = maxOf(0, k-(i-1))
val r = minOf(k, (i-1)*(i-2)/2)
if (l > r) continue
ndp[k] = (pre[r+1] - pre[l] + MOD) % MOD
}
if (req[i-1] != -1) {
val tmp = IntArray(maxInv+1)
tmp[req[i-1]] = ndp[req[i-1]]
for (j in 0..maxInv) ndp[j] = tmp[j]
}
dp = ndp
}
return dp.sum() % MOD
}
}
Rust
impl Solution {
pub fn count_inversions(n: i32, requirements: Vec<Vec<i32>>) -> i32 {
const MOD: i32 = 1_000_000_007;
let n = n as usize;
let mut req = vec![-1; n];
for r in requirements.iter() {
req[r[0] as usize] = r[1];
}
let max_inv = n*(n-1)/2;
let mut dp = vec![0; max_inv+1];
dp[0] = 1;
for i in 1..=n {
let mut ndp = vec![0; max_inv+1];
let mut pre = vec![0; max_inv+2];
for k in 0..=(i-1)*(i-2)/2 {
pre[k+1] = (pre[k] + dp[k]) % MOD;
}
for k in 0..=i*(i-1)/2 {
let l = k.saturating_sub(i-1);
let r = k.min((i-1)*(i-2)/2);
if l > r { continue; }
ndp[k] = (pre[r+1] - pre[l] + MOD) % MOD;
}
if req[i-1] != -1 {
let mut tmp = vec![0; max_inv+1];
tmp[req[i-1] as usize] = ndp[req[i-1] as usize];
ndp = tmp;
}
dp = ndp;
}
dp.iter().fold(0, |acc, &v| (acc + v) % MOD)
}
}
TypeScript
class Solution {
countInversions(n: number, requirements: number[][]): number {
const MOD = 1_000_000_007;
const req = Array(n).fill(-1);
for (const [endi, cnti] of requirements) req[endi] = cnti;
const maxInv = n*(n-1)/2;
let dp = Array(maxInv+1).fill(0);
dp[0] = 1;
for (let i = 1; i <= n; ++i) {
const ndp = Array(maxInv+1).fill(0);
const pre = Array(maxInv+2).fill(0);
for (let k = 0; k <= (i-1)*(i-2)/2; ++k) pre[k+1] = (pre[k] + dp[k]) % MOD;
for (let k = 0; k <= i*(i-1)/2; ++k) {
const l = Math.max(0, k-(i-1)), r = Math.min(k, (i-1)*(i-2)/2);
if (l > r) continue;
ndp[k] = (pre[r+1] - pre[l] + MOD) % MOD;
}
if (req[i-1] !== -1) {
const tmp = Array(maxInv+1).fill(0);
tmp[req[i-1]] = ndp[req[i-1]];
for (let j = 0; j <= maxInv; ++j) ndp[j] = tmp[j];
}
dp = ndp;
}
return dp.reduce((a, b) => (a + b) % MOD, 0);
}
}
Complexity
- ⏰ Time complexity:
O(n^2)due to DP and prefix sum optimizations. - 🧺 Space complexity:
O(n^2)for DP arrays.