【LeetCode】878、第 N 个神奇数字

【LeetCode】878、第 N 个神奇数字


一、最大公约数 最小公倍数 二分 容斥原理 同余原理

1.1 最大公约数 最小公倍数 二分 容斥原理 同余原理

第n个神奇数字x, 等价于求小于x的神奇数字有n个

因为x越大,则n越大,即二者为单调递增关系。所以可以二分x,求<=x的n值

对每个x而言,根据容斥定理,n为 x/a + x/b - x/lcm(a,b)

据此二分查找即可: 下界为min(a,b),上界为min(a,b) * n。

需注意二分查找的边界条件,见下文代码注释。

// go
func nthMagicalNumber(n int, a int, b int) int {
    lcm := a / gcd(a, b) * b // 最小公倍数, 例如 12 和 18 的最小公倍数为 (12 / 6) * 18, 也可以理解为 (12 / 6) * (18 / 6) * 6
    // 左开又开区间 (l, r)
    l := 0 // 因为是正整数, 所以 需 > 0, 所以下限为 0
    r := min(a, b) * n + 1 // a * n 内有 n 个可被a整除的数字, b * n 内有 n 个可被b整除的数字, 所以 若能被a或b整除, 则因为b的加入会更快的到第n个数, 所以第n个数的上限为 min(a, b) * n + 1, PS: 这个+1可加可不加
    for l+1 < r { // 开区间 (l, r)
        m := l + (r-l)/2
        // 容斥原理 得到 从0...m 有几个 神奇的数字
        if m/a + m/b - m/lcm >= n { // 若 >= n 个, 则需记录答案, 并向左找第 n 个
        // 这里需包括等号(意为等于时也要向左继续二分)。因为整数的除法可能除不尽,所以不同的mid,却算出相同的mid/a+mid/b-mid/lcm。例如a=2,b=3时,mid为6或7算出的mid/a+mid/b-mid/lcm均为4
            r = m
        } else { // 若 < n 个, 则需向右找第 n 个
            l = m
        }
    }
    return r % (1e9+7) // 注意: 是1e9而不是10e9
}

// 最大公约数
// 如 a = 12, b = 18, gcd = 6
//
// 具体过程如下:
// a, b = 12, 18
// a, b = 6, 12
// a, b = 0, 6
// 返回 b = 6
func gcd(a, b int) int {
    // if b == 0 {return a}
    // return gcd(b, a % b)
    for a != 0 {
        a, b = b % a, a
    }
    return b // a == 0 时, 则只能返回 b 啦, 肯定不能返回 a 啦
}

参考左神视频
参考图解
参考二分查找

二、多语言解法

C p p / G o / P y t h o n / R u s t / J s / T s Cpp/Go/Python/Rust/Js/Ts Cpp/Go/Python/Rust/Js/Ts

// cpp
// go 同上
# python
// rust
// js
// ts
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呆呆的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值