英文原文
A positive integer is magical if it is divisible by either a
or b
.
Given the three integers n
, a
, and b
, return the nth
magical number. Since the answer may be very large, return it modulo 109 + 7
.
Example 1:
Input: n = 1, a = 2, b = 3 Output: 2
Example 2:
Input: n = 4, a = 2, b = 3 Output: 6
Example 3:
Input: n = 5, a = 2, b = 4 Output: 10
Example 4:
Input: n = 3, a = 6, b = 4 Output: 8
Constraints:
1 <= n <= 109
2 <= a, b <= 4 * 104
中文题目
如果正整数可以被 A 或 B 整除,那么它是神奇的。
返回第 N 个神奇数字。由于答案可能非常大,返回它模 10^9 + 7
的结果。
示例 1:
输入:N = 1, A = 2, B = 3 输出:2
示例 2:
输入:N = 4, A = 2, B = 3 输出:6
示例 3:
输入:N = 5, A = 2, B = 4 输出:10
示例 4:
输入:N = 3, A = 6, B = 4 输出:8
提示:
1 <= N <= 10^9
2 <= A <= 40000
2 <= B <= 40000
通过代码
官方题解
方法一:数学方法
思路和算法
用数学方法找出第 $N$ 个神奇数字。
神奇数字是有规律。设 $L$ 为 $A$,$B$ 的最小公倍数,如果 $X \leq L$ 是神奇数字,那么 $X + L$ 也是神奇数字,因为 $A | X$, $A | L$ 可以推出 $A | (X + L)$,对于 $B$ 也是如此。
有 $M = \frac{L}{A} + \frac{L}{B} - 1$ 个神奇数字小于等于 $L$: 其中 $\frac{L}{A}$ 个是能被 $A$ 整除的,$\frac{L}{B}$ 个能被 $B$ 整除,$1$ 个能同时被 $A,B$ 整除。
设 $N = Mq + r$($r < M$),前 $Lq$ 个数字有 $Mq$ 个神奇数字,$(Lq + 1, Lq + 2, \cdots)$ 之间有 $r$ 个神奇数字。可以暴力搜 $r$,下一个神奇数字要么是 $Lq + A$ 要么是 $L*q + B$,依此类推。
class Solution {
public int nthMagicalNumber(int N, int A, int B) {
int MOD = 1_000_000_007;
int L = A / gcd(A, B) * B;
int M = L / A + L / B - 1;
int q = N / M, r = N % M;
long ans = (long) q * L % MOD;
if (r == 0)
return (int) ans;
int[] heads = new int[]{A, B};
for (int i = 0; i < r - 1; ++i) {
if (heads[0] <= heads[1])
heads[0] += A;
else
heads[1] += B;
}
ans += Math.min(heads[0], heads[1]);
return (int) (ans % MOD);
}
public int gcd(int x, int y) {
if (x == 0) return y;
return gcd(y % x, x);
}
}
class Solution(object):
def nthMagicalNumber(self, N, A, B):
from fractions import gcd
MOD = 10**9 + 7
L = A / gcd(A, B) * B
M = L / A + L / B - 1
q, r = divmod(N, M)
if r == 0:
return q * L % MOD
heads = [A, B]
for _ in xrange(r - 1):
if heads[0] <= heads[1]:
heads[0] += A
else:
heads[1] += B
return (q * L + min(heads)) % MOD
class Solution {
public:
int nthMagicalNumber(int N, int A, int B) {
int MOD = 1e9 + 7;
int L = A / gcd(A, B) * B;
int M = L / A + L / B - 1;
int q = N / M, r = N % M;
long ans = (long) q * L % MOD;
if (r == 0)
return (int) ans;
int heads[2] = {A, B};
for (int i = 0; i < r - 1; ++i) {
if (heads[0] <= heads[1])
heads[0] += A;
else
heads[1] += B;
}
ans += min(heads[0], heads[1]);
return (int) (ans % MOD);
}
int gcd(int x, int y) {
if (x == 0) return y;
return gcd(y % x, x);
}
};
var nthMagicalNumber = function(N, A, B) {
gcd = (x, y) => {
if (x == 0) return y;
return gcd(y % x, x);
}
const MOD = 1000000007;
const L = A / gcd(A, B) * B;
const M = L / A + L / B - 1;
const q = Math.trunc(N / M), r = N % M;
let ans = q * L % MOD;
if (r == 0)
return ans;
let heads = [A, B];
for (let i = 0; i < r - 1; ++i) {
if (heads[0] <= heads[1])
heads[0] += A;
else
heads[1] += B;
}
ans += Math.min(heads[0], heads[1]);
return ans % MOD;
};
复杂度分析
时间复杂度: $O(A + B)$,数学计算复杂度为 $O(1)$,计算 $q*M$ 后的 $r$ 个神奇数字的复杂度为 $O(M)$,即 $O(A+B)$。
空间复杂度: $O(1)$。
方法二: 二分搜索
思路
小于等于 $x$ 的神奇数字的个数是一个单调递增函数,可以用二分搜索来做这道题。
算法
设 $L = lcm(A, B)$,为 $A$,$B$ 的 最小公倍数,$L = \frac{A * B}{gcd(A, B)}$。
$f(x)$ 为小于等于 $x$ 的神奇数字的个数。$f(x) = \lfloor \frac{x}{A} \rfloor + \lfloor \frac{x}{B} \rfloor - \lfloor \frac{x}{L} \rfloor$。有 $\lfloor \frac{x}{A} \rfloor$ 个数字能被 $A$ 整除的,$\lfloor \frac{x}{B} \rfloor$ 个数字能被 $B$ 整除,同时需要减去 $\lfloor \frac{x}{L} \rfloor$ 个能被 $A$,$B$ 整除的数。
class Solution {
public int nthMagicalNumber(int N, int A, int B) {
int MOD = 1_000_000_007;
int L = A / gcd(A, B) * B;
long lo = 0;
long hi = (long) 1e15;
while (lo < hi) {
long mi = lo + (hi - lo) / 2;
// If there are not enough magic numbers below mi...
if (mi / A + mi / B - mi / L < N)
lo = mi + 1;
else
hi = mi;
}
return (int) (lo % MOD);
}
public int gcd(int x, int y) {
if (x == 0) return y;
return gcd(y % x, x);
}
}
class Solution(object):
def nthMagicalNumber(self, N, A, B):
from fractions import gcd
MOD = 10**9 + 7
L = A / gcd(A,B) * B
def magic_below_x(x):
#How many magical numbers are <= x?
return x / A + x / B - x / L
lo = 0
hi = 10**15
while lo < hi:
mi = (lo + hi) / 2
if magic_below_x(mi) < N:
lo = mi + 1
else:
hi = mi
return lo % MOD
class Solution {
public:
int nthMagicalNumber(int N, int A, int B) {
int MOD = 1e9 + 7;
int L = A / gcd(A, B) * B;
long lo = 0;
long hi = (long) 1e15;
while (lo < hi) {
long mi = lo + (hi - lo) / 2;
// If there are not enough magic numbers below mi...
if (mi / A + mi / B - mi / L < N)
lo = mi + 1;
else
hi = mi;
}
return (int) (lo % MOD);
}
int gcd(int x, int y) {
if (x == 0) return y;
return gcd(y % x, x);
}
};
var nthMagicalNumber = function(N, A, B) {
gcd = (x, y) => {
if (x == 0) return y;
return gcd(y % x, x);
}
const MOD = 1000000007;
const L = A / gcd(A, B) * B;
let lo = 0;
let hi = 1e15;
while (lo < hi) {
let mi = lo + Math.trunc((hi - lo) / 2);
// If there are not enough magic numbers below mi...
if (Math.trunc(mi/A) + Math.trunc(mi/B) - Math.trunc(mi/L) < N)
lo = mi + 1;
else
hi = mi;
}
return lo % MOD;
};
复杂度分析
时间复杂度: $O(\log(N * \max(A, B)))$。
空间复杂度: $O(1)$。
统计信息
通过次数 | 提交次数 | AC比率 |
---|---|---|
4555 | 16131 | 28.2% |
提交历史
提交时间 | 提交结果 | 执行时间 | 内存消耗 | 语言 |
---|