神奇数字 题解

题目描述

给定 ab 。若一个正整数能够被 a 整除或者被 b 整除,则我们称该正整数为神奇数字。现在给定 n ,请你求出第 n 个神奇数字。

答案可能很大,输出对答案 mod 10^9+7

输入格式

第一行三个整数 n,a,b

输出格式

输出一行,代表答案。

思路

既然让我们求第 n 个神奇数字,我们二分寻找神奇数字,然后计算当前数字前神奇数字的个数,以此来移动左右指针。
那问题就变成了如何求解一个数字前的所有神奇数字个数。设这个数字为 x ,可能会想到 x/a+x/b 。但是有个问题,可能会有重复情况,例如 n=28,a=4,b=6
a=4 : 4 8 12 16 20 24 28
a=6 : 6 12 18 24
容易发现,重复的部分就是a和b的最小公倍数的倍数 lcm(a,b) 。所以 x 前的神奇数字个数就=n/a+n/b-n/lcm(a,b)
lcm(a,b)=a*b/gcd(a,b) ,能用递归求得 gcd ,进而求得 lcm(a,b)
接下来考虑二分。神奇数字不可能超过 n*min(a,b) ,因为最坏情况下,第 n 个神奇数字就是 a,b 中较小的那个数的 n 倍。所以初始 l=0r=n*min(a,b)
get(n,a,b)=n/a+n/b-n/lcm(a,b) 。若 get(n,a,b)<nl=mid+1 ,否则 r=mid-1 。这里需要注意的是不能直接判断 get(n,a,b)==n ,因为 x 前神奇数字为 n 个的数可能有多个,直到 l=r 时才能确定第 n 个神奇数字。

注意事项

  • 不开 long long 见祖宗!我就是之前被坑了, WA 0 QwQ
  • 要对 1000000007 取模。但是计算神奇数字个数时不能取模,会导致答案计算错误。

AC代码

#include <bits/stdc++.h>//万能头yyds
#define int long long //开long long,这里我一开始全写的int,懒得改了
using namespace std;
const int mod=1e9+7; //取模
int gcd(int a,int b){ //最大公因数
    return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b){ //最小公倍数
    return a*b/gcd(a,b);
}
int get(int mid,int a,int b){ //mid前的神奇数字个数
    return mid/a+mid/b-mid/lcm(a,b);
}
signed main(){ //因为define int long long 了,所以要用signed定义main函数,否则会报错
    int n,a,b;
    cin>>n>>a>>b;
    int l=1,r=n*min(a,b); //二分开始
    while(l<=r){
        int mid=(l+r)/2;
        if(get(mid,a,b)<n){
            l=mid+1;
        }else{
            r=mid-1;
        }
    }
    cout<<l%mod<<endl;
    return 0; //好习惯
}

希望对大家有所帮助~

3 个赞

提个小建议:不要直接贴AC代码。
可以写一些伪代码或WA,TLE,RE之类的有错代码。

或者说发思路