Find the Count of Monotonic Pairs I — Problem & Solution
Find the Count of Monotonic Pairs I solution explained: step-by-step approach, complexity and code in Python, Java, C++ and JavaScript. A hard problem.
Find the Count of Monotonic Pairs I is a hard coding problem on arrays, dynamic programming and math, asked in Google interviews.
- Difficulty: Hard
- Topics: Arrays, Dynamic Programming, Math, Prefix Sum, Combinatorics
- Asked at: Google
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
You are given an array nums of n positive integers.
A pair of non-negative integer arrays (arr1, arr2), each of length n, is called monotonic when:
arr1is non-decreasing:arr1[0] <= arr1[1] <= ... <= arr1[n - 1],arr2is non-increasing:arr2[0] >= arr2[1] >= ... >= arr2[n - 1], andarr1[i] + arr2[i] == nums[i]for every indexi.
Return the number of monotonic pairs. Since the count can be large, return it modulo 10^9 + 7.
Example 1
Input: nums = [2,3,2]
Output: 4
Explanation: The pairs are ([0,1,1],[2,2,1]), ([0,1,2],[2,2,0]), ([0,2,2],[2,1,0]) and ([1,2,2],[1,1,0]).
Example 2
Input: nums = [5,5,5,5]
Output: 126
Example 3
Input: nums = [1]
Output: 2
Explanation: Either arr1 = [0] and arr2 = [1], or arr1 = [1] and arr2 = [0].
Constraints
1 <= n == nums.length <= 20001 <= nums[i] <= 50
Hints for Find the Count of Monotonic Pairs I
Hint 1
Once
arr1is fixed,arr2is forced (arr2[i] = nums[i] - arr1[i]), so count the valid choices ofarr1alone.Hint 2
Let
dp[i][v]be the number of valid prefixes ending witharr1[i] = v. Write down which previous valuesu = arr1[i - 1]are allowed:u <= vandnums[i - 1] - u >= nums[i] - v.Hint 3
Both conditions say
u <= v - max(0, nums[i] - nums[i - 1])(andu <= nums[i - 1]), sodp[i][v]is a prefix sum ofdp[i - 1].
How to solve Find the Count of Monotonic Pairs I
Choosing arr1 decides arr2, so the task is to count non-decreasing arrays arr1 with 0 <= arr1[i] <= nums[i] whose complement nums[i] - arr1[i] is non-increasing. A DP over the value of arr1[i] counts them, and the allowed previous values always form a prefix, so each state is one prefix-sum lookup.
Approach
- Start with
dp[v] = 1for everyvin0..nums[0]. - For each next index
i, letd = max(0, nums[i] - nums[i - 1])and build prefix sumspreof the currentdp. - For each
vin0..nums[i], the previous valueumay be anything up tomin(v - d, nums[i - 1]); setndp[v] = pre[that bound], or 0 whenv - d < 0. - After the last index, the answer is the sum of
dpmodulo10^9 + 7.
Why it works
arr1 non-decreasing means u <= v. arr2 non-increasing means nums[i - 1] - u >= nums[i] - v, i.e. u <= v - (nums[i] - nums[i - 1]). When nums[i] <= nums[i - 1] the second bound is weaker than the first, so the binding bound is v - max(0, nums[i] - nums[i - 1]) — always a prefix of the previous states, which is what makes the prefix sum exact.
Complexity
- Time —
O(n · M) - Space —
O(M)
Pitfalls
arr1andarr2are non-negative, sovonly ranges over0..nums[i]; the boundu <= nums[i - 1]matters whenv - dexceeds the previous range.- Reduce the prefix sums modulo
10^9 + 7as you go — adding up to 51 counts near the modulus overflows a 32-bit integer.
Reference solution
Python
from typing import List
def countOfPairs(nums: List[int]) -> int:
MOD = 10**9 + 7
dp = [1] * (nums[0] + 1)
for i in range(1, len(nums)):
d = max(0, nums[i] - nums[i - 1])
pre = []
s = 0
for x in dp:
s = (s + x) % MOD
pre.append(s)
ndp = [0] * (nums[i] + 1)
for v in range(nums[i] + 1):
top = min(v - d, nums[i - 1])
if top >= 0:
ndp[v] = pre[top]
dp = ndp
return sum(dp) % MODJava
public static int countOfPairs(int[] nums) {
final long MOD = 1000000007L;
long[] dp = new long[nums[0] + 1];
Arrays.fill(dp, 1L);
for (int i = 1; i < nums.length; i++) {
int d = Math.max(0, nums[i] - nums[i - 1]);
long[] pre = new long[dp.length];
long s = 0;
for (int u = 0; u < dp.length; u++) { s = (s + dp[u]) % MOD; pre[u] = s; }
long[] nd = new long[nums[i] + 1];
for (int v = 0; v <= nums[i]; v++) {
int top = Math.min(v - d, nums[i - 1]);
nd[v] = top >= 0 ? pre[top] : 0;
}
dp = nd;
}
long total = 0;
for (long x : dp) total = (total + x) % MOD;
return (int) total;
}C++
int countOfPairs(vector<int>& nums) {
const long long MOD = 1000000007LL;
vector<long long> dp(nums[0] + 1, 1);
for (size_t i = 1; i < nums.size(); i++) {
int d = max(0, nums[i] - nums[i - 1]);
vector<long long> pre(dp.size());
long long s = 0;
for (size_t u = 0; u < dp.size(); u++) { s = (s + dp[u]) % MOD; pre[u] = s; }
vector<long long> nd(nums[i] + 1, 0);
for (int v = 0; v <= nums[i]; v++) {
int top = min(v - d, nums[i - 1]);
if (top >= 0) nd[v] = pre[top];
}
dp = nd;
}
long long total = 0;
for (long long x : dp) total = (total + x) % MOD;
return (int) total;
}JavaScript
var countOfPairs = function(nums) {
var MOD = 1000000007;
var dp = [];
for (var v = 0; v <= nums[0]; v++) dp.push(1);
for (var i = 1; i < nums.length; i++) {
var d = Math.max(0, nums[i] - nums[i - 1]);
var pre = [], s = 0;
for (var u = 0; u < dp.length; u++) { s = (s + dp[u]) % MOD; pre.push(s); }
var nd = [];
for (var w = 0; w <= nums[i]; w++) {
var top = Math.min(w - d, nums[i - 1]);
nd.push(top >= 0 ? pre[top] : 0);
}
dp = nd;
}
var total = 0;
for (var t = 0; t < dp.length; t++) total = (total + dp[t]) % MOD;
return total;
};Also on the editorial tab: C, C#, Go, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.
All 1199 arrays problems · the whole catalogue
Learn the technique: Arrays · Dynamic Programming