题目:
一个正整数如果能被 a 或 b 整除,那么它是神奇的。 给定三个整数 n , a , b ,返回第 n 个神奇的数字。因为答案可能很大,所以返回答案 对 109 + 7 取模 后的值。
示例 1:
输入:n = 1, a = 2, b = 3
输出:2
示例 2:
输入:n = 4, a = 2, b = 3
输出:6
提示:
1 <= n <= 10^9
2 <= a, b <= 4 * 10^4
理解
题目定义了一个MagicalNumber(神奇数),能被a整除 或 被b整除的数成为MagicalNumber.
输入: n, a, b 三个整数
输出: 第n个神奇数
思路
一般思路会使用双指针分别指向a的倍数数组和b倍数的数组,遍历到第n个.这种算法的时间复杂度为O(n),但题目给的范围最大为10^9,肯定要超时的.所以降时间复杂度.
容斥原理 + 二分查找
容斥原理:
将所有符合条件的都计算出来,再将重复的减去.这道题,可以把所有能被a和b整除的都计算出来,然后减去既能被a整除又能被b整除的(相当于能被a和b的最小公倍数整除).
二分查找:
因为二分可以把复杂度降到Log(n),所以可以使用二分查找第n个神奇数.
代码
public class Solution878 {
static final long MOD = 1000000007;
//二分查找
public int nthMagicalNumber(int n, int a, int b) {
int c = lcm(a,b);
long l = 0, r = (long) 1e18;
while(l<r){
long mid = (l+r)/2;
if(count(mid,a,b,c)>=n){
r = mid;
}else{
l = mid + 1;
}
}
return (int)(r%MOD);
}
/*
根据容斥原理计算[0,n]中有多少个数可以被2或3整除
*/
private static long count(long n,int a,int b,int c){
return n/a + n/b - n/c;
}
//求两数的最大公倍数
private static int gcd(int a, int b){
return b==0 ? a : gcd(b,a%b);
}
//求最小公倍数
private static int lcm(int a,int b){
return a*b / gcd(a,b);
}
}