Count the Number of Ideal Arrays
HardPrompt
You are given two integers n and maxValue, which are used to describe an ideal array.
A 0-indexed integer array arr of length n is considered ideal if the following conditions hold:
- Every
arr[i]is a value from1tomaxValue, for0 <= i < n. - Every
arr[i]is divisible byarr[i - 1], for0 < i < n.
Return the number of distinct ideal arrays of length n. Since the answer may be very large, return it modulo 109 + 7.
Example 1:
Input: n = 2, maxValue = 5
Output: 10
Explanation: The following are the possible ideal arrays:
- Arrays starting with the value 1 (5 arrays): [1,1], [1,2], [1,3], [1,4], [1,5]
- Arrays starting with the value 2 (2 arrays): [2,2], [2,4]
- Arrays starting with the value 3 (1 array): [3,3]
- Arrays starting with the value 4 (1 array): [4,4]
- Arrays starting with the value 5 (1 array): [5,5]
There are a total of 5 + 2 + 1 + 1 + 1 = 10 distinct ideal arrays.Example 2:
Input: n = 5, maxValue = 3
Output: 11
Explanation: The following are the possible ideal arrays:
- Arrays starting with the value 1 (9 arrays):
- With no other distinct values (1 array): [1,1,1,1,1]
- With 2nd distinct value 2 (4 arrays): [1,1,1,1,2], [1,1,1,2,2], [1,1,2,2,2], [1,2,2,2,2]
- With 2nd distinct value 3 (4 arrays): [1,1,1,1,3], [1,1,1,3,3], [1,1,3,3,3], [1,3,3,3,3]
- Arrays starting with the value 2 (1 array): [2,2,2,2,2]
- Arrays starting with the value 3 (1 array): [3,3,3,3,3]
There are a total of 9 + 1 + 1 = 11 distinct ideal arrays.
Constraints:
2 <= n <= 1041 <= maxValue <= 104
Approaches
3 approaches with complexity analysis and trade-offs.
This approach uses dynamic programming where the state dp[i][j] represents the number of ideal arrays of length i that end with the value j. We build up the solution for length n by iteratively computing the results for lengths from 1 to n.
Algorithm
- Define a 2D DP array
dp[i][j]to store the number of ideal arrays of lengthiending with valuej. - The state transition is
dp[i][j] = sum(dp[i-1][k])for allkthat are divisors ofj. - The base case is
dp[1][j] = 1for all1 <= j <= maxValue. - To optimize the transition, instead of finding divisors for each
j, iterate throughkfrom the previous state and adddp[i-1][k]to all its multiplesm*k. - Since
dp[i]only depends ondp[i-1], we can optimize space by using only two rows (or one) of the DP table. - The final answer is the sum of all
dp[n][j]forjfrom 1 tomaxValue.
Walkthrough
We can define dp[i][j] as the number of ideal arrays of length i ending with value j.
-
Base Case: For an array of length 1, any value
jfrom 1 tomaxValueis valid. So,dp[1][j] = 1for1 <= j <= maxValue. -
Recurrence Relation: For an ideal array of length
i > 1ending inj, say[a_0, ..., a_{i-2}, j], the previous elementa_{i-2}must be a divisor ofj. Thus, the number of such arrays is the sum of the counts of ideal arrays of lengthi-1ending in any divisor ofj. The recurrence is:dp[i][j] = sum(dp[i-1][k])for allksuch thatk | j. -
Optimization: A naive implementation of this recurrence would be too slow. Instead of iterating through divisors of
jto pull values fromdp[i-1], we can iterate throughkfrom 1 tomaxValueand adddp[i-1][k]todp[i][j]for all multiplesjofk. This is more efficient. -
Space Optimization: Notice that
dp[i]only depends ondp[i-1]. We can optimize space fromO(n * maxValue)toO(maxValue)by using only two arrays, one for the previous state and one for the current state. -
Final Answer: The total number of ideal arrays is the sum of
dp[n][j]for alljfrom 1 tomaxValue.
class Solution { public int idealArrays(int n, int maxValue) { long MOD = 1_000_000_007; long[] dp = new long[maxValue + 1]; Arrays.fill(dp, 1); dp[0] = 0; // 0 is not a valid value for (int i = 2; i <= n; i++) { long[] newDp = new long[maxValue + 1]; for (int k = 1; k <= maxValue; k++) { if (dp[k] == 0) continue; for (int j = k; j <= maxValue; j += k) { newDp[j] = (newDp[j] + dp[k]) % MOD; } } dp = newDp; } long totalCount = 0; for (int j = 1; j <= maxValue; j++) { totalCount = (totalCount + dp[j]) % MOD; } return (int) totalCount; }}Complexity
Time
O(n * maxValue * log(maxValue)) The outer loop runs `n-1` times. The inner loops iterate through all numbers and their multiples up to `maxValue`. The complexity of the inner part is `sum_{k=1 to maxValue} (maxValue/k)`, which is `O(maxValue * log(maxValue))`. The total time is `O(n * maxValue * log(maxValue))`, which is too slow for the given constraints.
Space
O(maxValue) We use an array of size `maxValue` to store the DP states for the current length, which dominates the space requirement.
Trade-offs
Pros
Conceptually simple and follows a standard DP pattern.
Space complexity is manageable.
Cons
The time complexity is high, making it too slow for the given constraints.
It recomputes a lot of information in each step of the length
i.
This approach observes that any ideal array is composed of a non-decreasing sequence of values. We can rephrase the problem as first choosing a sequence of k distinct values d_1 < d_2 < ... < d_k that satisfy the divisibility condition, and then arranging them into an n-length array. The number of distinct values k is small, which makes this approach more efficient.
Algorithm
- The problem can be reframed as: first, choose a sequence of
kdistinct valuesd_1 < d_2 < ... < d_ksatisfyingd_i | d_{i+1}andd_k <= maxValue. Second, arrange thesekvalues into ann-length array. - The number of ways to arrange
kdistinct values into ann-length ideal array isC(n-1, k-1). - The total count is
sum_{k=1 to n} (ways[k] * C(n-1, k-1)), whereways[k]is the number of valid sequences ofkdistinct values. - Use DP to find
ways[k]. Letdp[v]be the number of valid sequences of the current length ending inv. - Iterate
kfrom 1 up tomin(n, log2(maxValue)). In each iteration, compute the newdparray based on the previous one. - The transition is:
new_dp[v] = sum(dp[u])for all proper divisorsuofv. - Precompute combinations
C(n, k).
Walkthrough
The core idea is to count ideal arrays based on the number of distinct values they contain.
-
Combinatorial Insight: An ideal array with
kdistinct valuesd_1, d_2, ..., d_kmust haved_1 < d_2 < ... < d_kandd_i | d_{i+1}. The number of ways to place thesekvalues into an array of lengthnis a stars-and-bars problem, equivalent to placingk-1dividers inn-1slots, which isC(n-1, k-1). -
Total Count Formula: The total number of ideal arrays is
sum_{k=1 to n} ways[k] * C(n-1, k-1), whereways[k]is the number of valid sequences ofkdistinct values. -
DP for
ways[k]: We can use dynamic programming to findways[k]. Letdp[v]store the number of valid sequences of a certain length ending with valuev. We can iterate on the lengthk.- For
k=1,dp[v] = 1for allv.ways[1] = maxValue. - For
k > 1, we compute a newnew_dparray.new_dp[v]is the sum ofdp[u]for all proper divisorsuofv. This can be computed efficiently by iterating throughuand addingdp[u]to its multiplesm*u(wherem > 1).
- For
-
Bounded
k: The number of distinct valueskis small. Sinced_i >= 2*d_{i-1}, we haved_k >= 2^(k-1). Asd_k <= maxValue,k-1 <= log2(maxValue), sokis at most~14formaxValue=10^4.
class Solution { long MOD = 1_000_000_007; long[][] C; public int idealArrays(int n, int maxValue) { // Precompute combinations C(n, k) C = new long[n + 1][15]; for (int i = 0; i <= n; i++) { C[i][0] = 1; for (int j = 1; j <= Math.min(i, 14); j++) { C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD; } } long[] dp = new long[maxValue + 1]; Arrays.fill(dp, 1); dp[0] = 0; long totalAns = maxValue; // For k=1, ways[1] = maxValue, C(n-1, 0) = 1 for (int k = 2; k <= Math.min(n, 14); k++) { long[] newDp = new long[maxValue + 1]; long ways_k = 0; for (int u = 1; u <= maxValue; u++) { if (dp[u] == 0) continue; for (int v = 2 * u; v <= maxValue; v += u) { newDp[v] = (newDp[v] + dp[u]) % MOD; } } dp = newDp; for (long count : dp) { ways_k = (ways_k + count) % MOD; } if (ways_k == 0) break; totalAns = (totalAns + ways_k * C[n - 1][k - 1]) % MOD; } return (int) totalAns; }}Complexity
Time
O(log(maxValue) * maxValue * log(maxValue)) The outer loop runs for `k` from 2 up to `min(n, ~log2(maxValue))`. The inner part to compute the next DP state takes `O(maxValue * log(maxValue))`. This makes the total time complexity better than the first approach.
Space
O(maxValue + n) We need `O(maxValue)` for the DP array and `O(n * log(maxValue))` for the combinations table. Since `log(maxValue)` is small, this is effectively `O(maxValue + n)`.
Trade-offs
Pros
Significantly more efficient than the previous approach for large
n.Leverages a key insight about the small number of distinct values.
Cons
The logic is more complex than the straightforward DP on length.
Still involves a
maxValue * log(maxValue)computation in a loop.
This is the most efficient approach, which relies on a combinatorial insight related to prime factorization. The divisibility constraint a[i-1] | a[i] can be analyzed independently for each prime factor's exponents. This transforms the problem of counting sequences of numbers into counting non-decreasing sequences of exponents for each prime, which has a direct combinatorial formula.
Algorithm
- The total number of ideal arrays is
sum_{v=1 to maxValue} (number of ideal arrays ending in v). - The problem can be decoupled for each prime factor. For an array
[a_0, ..., a_{n-1}]ending inv, the sequence of exponents of any primepmust be non-decreasing:exp_p(a_0) <= ... <= exp_p(a_{n-1}) = exp_p(v). - The number of such non-decreasing exponent sequences of length
nfor a primepwith final exponente_pisC(e_p + n - 1, n - 1). - The total number of ways for a fixed
vis the product of these counts over all its prime factors:product_{p|v} C(exp_p(v) + n - 1, n - 1). - Precompute factorials and their modular inverses to calculate combinations in
O(1). - Use a sieve to precompute the Smallest Prime Factor (SPF) for all numbers up to
maxValue. - Iterate
vfrom 1 tomaxValue, find its prime factorization using the SPF array inO(log v)time, calculate the ways forv, and add to the total answer.
Walkthrough
This method decouples the problem by considering the prime factorization of the array elements.
-
Decomposition by Prime Factors: An ideal array
[a_0, a_1, ..., a_{n-1}]satisfiesa_{i-1} | a_i. This is equivalent to saying that for every primep, the sequence of exponents ofpin the prime factorization of the array elements is non-decreasing. That is,exp_p(a_0) <= exp_p(a_1) <= ... <= exp_p(a_{n-1}). -
Counting for a Fixed End Value: Let's count the number of ideal arrays of length
nthat end with a specific valuev. Let the prime factorization ofvbep_1^{e_1} * p_2^{e_2} * ... * p_r^{e_r}. For each primep_j, we need to find the number of non-decreasing sequences of exponents of lengthn,(exp_0, ..., exp_{n-1}), such that0 <= exp_0 <= ... <= exp_{n-1} = e_j. This is equivalent to counting non-decreasing sequences of lengthn-1with elements from{0, ..., e_j}. Using stars and bars, the number of such sequences isC(e_j + (n-1), n-1). -
Total Ways for
v: Since the choice of exponent sequences for different primes are independent, we can multiply their counts. The number of ideal arrays ending invisproduct_{j=1 to r} C(e_j + n - 1, n - 1). -
Algorithm:
- Precompute factorials and modular inverses to calculate
C(n, k)inO(1). The maximum value forninC(n,k)will bemaxValue + n - 1but we only needC(a+n-1, n-1)whereais an exponent, soa <= log2(maxValue). The upper bound for combinations isC(log2(maxValue) + n - 1, n - 1). We need factorials up ton + maxValue. - Use a sieve to find the Smallest Prime Factor (SPF) for all numbers up to
maxValue. - Iterate
vfrom 1 tomaxValue. For eachv, find its prime factorization using the SPF array. For each prime factorpwith exponente, calculateC(e + n - 1, n - 1)and multiply these results to get the ways forv. - Sum up the ways for all
vto get the final answer.
- Precompute factorials and modular inverses to calculate
class Solution { long MOD = 1_000_000_007; long[] fact; long[] invFact; public int idealArrays(int n, int maxValue) { int limit = n + maxValue; fact = new long[limit]; invFact = new long[limit]; fact[0] = 1; invFact[0] = 1; for (int i = 1; i < limit; i++) { fact[i] = (fact[i - 1] * i) % MOD; invFact[i] = power(fact[i], MOD - 2); } int[] spf = new int[maxValue + 1]; for (int i = 2; i <= maxValue; i++) { if (spf[i] == 0) { spf[i] = i; for (long j = (long)i * i; j <= maxValue; j += i) { if (spf[(int)j] == 0) { spf[(int)j] = i; } } } } long totalAns = 0; for (int v = 1; v <= maxValue; v++) { long ways_v = 1; int temp_v = v; while (temp_v > 1) { int p = spf[temp_v]; int count = 0; while (temp_v % p == 0) { count++; temp_v /= p; } ways_v = (ways_v * nCr(count + n - 1, n - 1)) % MOD; } totalAns = (totalAns + ways_v) % MOD; } return (int) totalAns; } private long nCr(int N, int R) { if (R < 0 || R > N) return 0; return (((fact[N] * invFact[R]) % MOD) * invFact[N - R]) % MOD; } private long power(long base, long exp) { long res = 1; base %= MOD; while (exp > 0) { if (exp % 2 == 1) res = (res * base) % MOD; base = (base * base) % MOD; exp /= 2; } return res; }}Complexity
Time
O(maxValue * log(maxValue) + n) The sieve precomputation takes `O(maxValue * log(log(maxValue)))`. Precomputing factorials takes `O(maxValue + n)`. The main loop runs from 1 to `maxValue`. Inside the loop, prime factorization using the SPF array takes `O(log v)`. The total time is dominated by the main loop, resulting in `O(maxValue * log(maxValue))`. The `+n` comes from factorial precomputation.
Space
O(maxValue + n) We need `O(maxValue)` for the Sieve (SPF array) and `O(maxValue + n)` for precomputed factorials and their inverses.
Trade-offs
Pros
Most efficient approach with the best time complexity.
Provides a closed-form formula for the number of arrays ending in a specific value.
Elegant solution based on number theory principles.
Cons
Requires knowledge of number theory concepts like prime factorization and modular arithmetic for combinations.
Implementation is more involved due to the need for a sieve and modular inverse calculations.
Solutions
Solution
class Solution {private int[][] f;private int[][] c;private int n;private int m;private static final int MOD = (int)1 e9 + 7;public int idealArrays(int n, int maxValue) { this.n = n; this.m = maxValue; this.f = new int[maxValue + 1][16]; for (int[] row : f) { Arrays.fill(row, -1); } c = new int[n][16]; for (int i = 0; i < n; ++i) { for (int j = 0; j <= i && j < 16; ++j) { c[i][j] = j == 0 ? 1 : (c[i - 1][j] + c[i - 1][j - 1]) % MOD; } } int ans = 0; for (int i = 1; i <= m; ++i) { ans = (ans + dfs(i, 1)) % MOD; } return ans; }private int dfs(int i, int cnt) { if (f[i][cnt] != -1) { return f[i][cnt]; } int res = c[n - 1][cnt - 1]; if (cnt < n) { for (int k = 2; k * i <= m; ++k) { res = (res + dfs(k * i, cnt + 1)) % MOD; } } f[i][cnt] = res; return res; }}class Solution {public: int m, n; const int mod = 1e9 + 7; vector<vector<int>> f; vector<vector<int>> c; int idealArrays(int n, int maxValue) { this->m = maxValue; this->n = n; f.assign(maxValue + 1, vector<int>(16, -1)); c.assign(n, vector<int>(16, 0)); for (int i = 0; i < n; ++i) for (int j = 0; j <= i && j < 16; ++j) c[i][j] = !j ? 1 : (c[i - 1][j] + c[i - 1][j - 1]) % mod; int ans = 0; for (int i = 1; i <= m; ++i) ans = (ans + dfs(i, 1)) % mod; return ans; } int dfs(int i, int cnt) { if (f[i][cnt] != -1) return f[i][cnt]; int res = c[n - 1][cnt - 1]; if (cnt < n) for (int k = 2; k * i <= m; ++k) res = (res + dfs(k * i, cnt + 1)) % mod; f[i][cnt] = res; return res; }};class Solution: def idealArrays(self, n: int, maxValue: int) -> int: @ cache def dfs(i, cnt): res = c[- 1][cnt - 1] if cnt < n: k = 2 while k * i <= maxValue: res = (res + dfs(k * i, cnt + 1)) % mod k += 1 return res c = [[0] * 16 for _ in range(n)] mod = 10 ** 9 + 7 for i in range(n): for j in range(min(16, i + 1)): c[i][j] = 1 if j == 0 else (c[i - 1][j] + c[i - 1][j - 1]) % mod ans = 0 for i in range(1, maxValue + 1): ans = (ans + dfs(i, 1)) % mod return ansVideo 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.