|
| 1 | +/** |
| 2 | + * 2338. Count the Number of Ideal Arrays |
| 3 | + * https://leetcode.com/problems/count-the-number-of-ideal-arrays |
| 4 | + */ |
| 5 | +export function idealArrays(n: number, maxValue: number): number { |
| 6 | + const MOD = 10 ** 9 + 7; |
| 7 | + |
| 8 | + /** |
| 9 | + * 점점 증가하는 이상적인 배열의 최대 길이는 14입니다. |
| 10 | + * 2^14 > 10,000 가능한 가장 긴 점점 증가하는 이상적 배열은 [1, 2, 4, 8, ..., 8192]입니다. |
| 11 | + */ |
| 12 | + const MAX_LEN = Math.min(n, 14); |
| 13 | + |
| 14 | + // dp[i][j]는 길이 i의 j로 끝나는 이상적인 배열의 개수 |
| 15 | + const dp = Array.from({ length: MAX_LEN + 1 }, () => new Array<number>(maxValue + 1).fill(0)); |
| 16 | + |
| 17 | + // `maxValue`까지 모든 수에 대한 약수 |
| 18 | + const divisors = getDivisors(maxValue); |
| 19 | + |
| 20 | + // 길이가 1인 이상적인 배열의 개수는 1로 초기화합니다. |
| 21 | + for (let i = 1; i <= maxValue; i++) { |
| 22 | + dp[1][i] = 1; |
| 23 | + } |
| 24 | + |
| 25 | + /** |
| 26 | + * dp[i][j] = sum(dp[i-1][k]) |
| 27 | + * 단, j는 k로 나누어 떨어져야 함 (즉, k는 j의 약수) |
| 28 | + */ |
| 29 | + for (let i = 2; i <= MAX_LEN; i++) { |
| 30 | + for (let j = 1; j <= maxValue; j++) { |
| 31 | + for (const k of divisors.get(j) ?? []) { |
| 32 | + dp[i][j] = (dp[i][j] + dp[i - 1][k]) % MOD; |
| 33 | + } |
| 34 | + } |
| 35 | + } |
| 36 | + |
| 37 | + // dp[i][0] = 길이가 i인 점점 증가하는 이상적인 배열의 개수 |
| 38 | + for (let i = 1; i <= MAX_LEN; i++) { |
| 39 | + for (let j = 1; j <= maxValue; j++) { |
| 40 | + dp[i][0] = (dp[i][0] + dp[i][j]) % MOD; |
| 41 | + } |
| 42 | + } |
| 43 | + |
| 44 | + /** |
| 45 | + * 조합론을 사용한, 길이가 i인 점점 증가하는 이상적인 배열의 개수로부터 |
| 46 | + * 길이가 n인 증가하는 이상적인 배열의 총 개수를 도출 |
| 47 | + */ |
| 48 | + let answer = 0n; |
| 49 | + for (let i = 1; i <= MAX_LEN; i++) { |
| 50 | + answer += nCk(n - 1, i - 1) * BigInt(dp[i][0]); |
| 51 | + answer %= BigInt(MOD); |
| 52 | + } |
| 53 | + return Number(answer); |
| 54 | +} |
| 55 | + |
| 56 | +// "n Choose k"를 계산 |
| 57 | +function nCk(n: number, k: number): bigint { |
| 58 | + let result = 1n; |
| 59 | + for (let i = 1; i <= k; i++) { |
| 60 | + result = (result * BigInt(n - (i - 1))) / BigInt(i); |
| 61 | + } |
| 62 | + return result; |
| 63 | +} |
| 64 | + |
| 65 | +// `maxValue`까지의 모든 수에 대한 약수들을 계산 |
| 66 | +function getDivisors(maxValue: number): Map<number, number[]> { |
| 67 | + const divisors = new Map<number, number[]>(); |
| 68 | + for (let value = 1; value <= maxValue; value++) { |
| 69 | + divisors.set(value, []); |
| 70 | + } |
| 71 | + for (let value = 1; value <= maxValue; value++) { |
| 72 | + let multiply = 2; |
| 73 | + while (value * multiply <= maxValue) { |
| 74 | + divisors.get(value * multiply)?.push(value); |
| 75 | + multiply += 1; |
| 76 | + } |
| 77 | + } |
| 78 | + return divisors; |
| 79 | +} |
0 commit comments