|
| 1 | +/** |
| 2 | + * @param {string} l |
| 3 | + * @param {string} r |
| 4 | + * @param {number} b |
| 5 | + * @return {number} |
| 6 | + */ |
| 7 | +var countNumbers = function (l, r, b) { |
| 8 | + const mod = 1000000007 |
| 9 | + |
| 10 | + const bigL = BigInt(l) |
| 11 | + const bigR = BigInt(r) |
| 12 | + |
| 13 | + const rightCnt = helper(bigR, b) |
| 14 | + const leftCnt = helper(bigL - 1n, b) |
| 15 | + const ans = (rightCnt - leftCnt + BigInt(mod)) % BigInt(mod) |
| 16 | + return Number(ans) |
| 17 | + function helper(num, b) { |
| 18 | + if (num < 0n) return 0n |
| 19 | + |
| 20 | + // b base digits |
| 21 | + const digits = Array.from(num.toString(b)).map((d) => BigInt(d) - 0n) |
| 22 | + const n = digits.length |
| 23 | + |
| 24 | + const seen = new Map() |
| 25 | + |
| 26 | + function dfs(pos, last, started, tight) { |
| 27 | + if (pos === n) return 1n |
| 28 | + |
| 29 | + const key = `${pos}, ${last}, ${started}, ${tight}` |
| 30 | + if (seen.has(key)) return seen.get(key) |
| 31 | + |
| 32 | + const limit = tight ? digits[pos] : BigInt(b - 1) |
| 33 | + let ways = 0n |
| 34 | + |
| 35 | + for (let d = 0n; d <= limit; d++) { |
| 36 | + if (started && d < last) continue |
| 37 | + |
| 38 | + const nextStarted = started || d !== 0n |
| 39 | + const nextLast = started || d !== 0n ? d : last |
| 40 | + |
| 41 | + const nextTight = tight && d === limit |
| 42 | + ways = |
| 43 | + (ways + dfs(pos + 1, nextLast, nextStarted, nextTight)) % BigInt(mod) |
| 44 | + } |
| 45 | + seen.set(key, ways % BigInt(mod)) |
| 46 | + return ways |
| 47 | + } |
| 48 | + |
| 49 | + return dfs(0, 0n, false, true) |
| 50 | + } |
| 51 | +} |
0 commit comments