我正在尝试解决关于LeetCode.com的问题:
如果正整数可以被
A
或B
整除,那么它是神奇的。
返回第N个幻数。由于答案可能非常大,请以10^9 + 7
为模返回。例如,对于N = 5, A = 2, B = 4
,输出为10
。
highly upvoted solution建议“搜索”答案。但是我不明白如何将其建模为搜索问题-也是二进制搜索问题。有人可以指出背后的直觉吗?
int nthMagicalNumber(int N, int A, int B) {
long lcm = A * B / __gcd(A, B), l = 2, r = 1e14, mod = 1e9 + 7;
while (l < r) {
long m = (l + r) / 2;
if (m / A + m / B - m / lcm < N) l = m + 1;
else r = m;
}
return l % mod;
}
谢谢!
答案 0 :(得分:1)
对于给定的数字m
,我们可以找出比m
小的幻数:加m/A
(被A
整除的数字的数量小于m
)和m/B
(小于B
的可被m
整除的数的计数)减去m/lcm
(可被整除的数的计数) A
和B
都小于m
,以避免重复计算)。这里的lcm
是A
和B
的最小公倍数;被A
和B
整除的所有数字都是lcm
的倍数。
从这里开始,使用二进制搜索很容易。从足够大的间隔开始(根据问题陈述中提供的输入界限,证明初始范围足够大,留给读者练习)。找到中间的m
,找出比该中间数小的幻数。如果该数量大于N
,则m
会超调,我们需要进一步考虑左半部分。否则,m
会下冲,我们会搜索右半部分。
此方法可以改进。给定序列X_i=i*A
和Y_i=i*B
,我们考虑序列Z
,它是X
和Y
的有序并集;我们的任务是找到Z_N
。显而易见,Z
可以用重复模式描述:到达L=lcm(A, B)
之后,相同的模式会重复自身上移L
,然后再次上移{{1 }}等。
例如,如果2*L
和A=2
,则序列B=3
如下所示:
Z
很容易看到四个数字的模式,一次又一次地重复,每次移动2 3 4 6 | 8 9 10 12 | 14 15 16 18 | ...
在通常情况下,此模式的长度为6 = L = LCM(2, 3)
:来自序列M = L/A + L/B - 1
的数字X
的数量加上序列<= L
的数量Y
减去1,因为<= L
本身被计算两次。因此,L
。
考虑到这一点,找到Z_{i+k*M} = Z_i + k*L
就足够了。为此,将二进制搜索应用于Z_N
范围就足够了-仅搜索重复模式的第一次出现,因为序列的其余部分可以轻松地从中得出。
答案 1 :(得分:0)
这个想法是,给定数字m
,可以通过添加m
的倍数来计算小于或等于A
的幻数的数量({{ 1}}),其中m / A
(B
)的倍数减去m / B
和A
(B
)的倍数数了两次。
现在解决方案就是最小的数字m / lcm
,其中小于或等于m
的幻数正好是m
。可以使用二进制搜索找到它。