For an integer array nums, an inverse pair is a pair of integers [i, j] where 0 <= i < j < nums.length and nums[i] > nums[j].
Given two integers n and k, return the number of different arrays consisting of numbers from 1 to n such that there are exactly k inverse pairs. Since the answer can be huge, return it modulo 109 + 7.
solutions
Ok, this one’s pretty crazy. The most important intuition is the following:
Given some array, the number of inverse pairs is equal to the number of shift-left transformations are done, when compared to the solely ascending array. (When considering shifted elements in ascending order).
For example, given the array [4,2,1,3], we see from a high level that 2 has been shifted left by one (to the left of 1), and 4 has been shifted three times. So, we have 4 inverse pairs (we can confirm this - (4,2), (4,1), (4,3), (2,1)).
naive dp
With the above intuition, we can start realizing that there is some sort of optimal substructure. Let represent the number of permutations of with inverse pairs.
Start by considering the base case of . We know that there is only one possible permutation, and it has 0 inverse pairs.
Now, consider adding to this array. We can insert either before or after the . If we insert before , we create an inverse pair. If we insert after , we do not.
This concept can be generalized. Given a subarray permutation of elements and inverse pairs, we have options to insert the next value, , into the array. If we insert at the leftmost possible position (index 0), our new array permutation has inverse pairs. On the opposite end, if we insert after all existing elements, our new array still has inverse pairs.
Generalized, if we insert at index , our new array has inverse pairs.
With this in mind, we can build up a solution to by first starting with , and build up each next row for until we reach our final value.
# O(n*nk) = O(n^2 k)
def kInversePairs(self, n: int, k: int) -> int:
# base case, n=1 for all values 0-k
dp = [1]+[0]*k
prev_n = 1
for _ in range(n-1):
new_row = [0]*(k+1)
for pk, count in enumerate(dp):
if count > 0:
for insertion in range(0, prev_n+1):
if prev_n+pk-insertion <= k:
new_row[prev_n+pk-insertion] += count
new_row[prev_n+pk-insertion] %= (10**9+7)
dp = new_row
prev_n += 1
return dp[-1]optimized dp w/ sliding window
In the above solution, I look through each value for the previous value, and then simulate each possible insertion to increment the new value () in the next row.
However, we importantly can realize that the value at is just the sum of .
To avoid an unnecessary loop for each value, we can maintain a sliding window sum from to , and setting to this cumulative sum.
# O(nk)
def kInversePairs(self, n: int, k: int) -> int:
# base case, n=1 for all values 0-k
dp = [1]+[0]*k
prev_n = 1
for _ in range(n-1):
new_row = [0]*(k+1)
window_sum = 0
for _k in range(k+1):
window_sum += dp[_k]
if _k > prev_n:
window_sum -= dp[_k-prev_n-1]
new_row[_k] = window_sum % (10**9+7)
dp = new_row
prev_n += 1
return dp[-1]