丑数 III

题目

题源
丑数 III

代码

码源

class Solution {
    public int nthUglyNumber(int n, int a, int b, int c) {
        if (a == 1 || b == 1 || c == 1) return n;

        long ab =  lcm(a, b);
        long ac =  lcm(a, c);
        long bc =  lcm(b, c);
        long abc =  lcm(ab, c);

        long left = Math.min(Math.min(a,b),c);
        long right = (long)Math.pow(left,n);// 上边界是这个最小者的n倍

        while (left <= right) {
            long mid = left + (right - left) / 2;
            long count = mid / a + mid / b + mid / c - mid / ab - mid / ac - mid / bc + mid / abc;
            if (count == n) {
                left = mid;
                break;
            } else if (count > n) {
                right = mid - 1;
                
            } else {
                left = mid + 1;
            }
        }
        // 比如第n个丑数是X,那么[X,X + min(a,b,c))这个半开区间内的所有数都同时包含n个丑数因子,我们通过二分法得到的答案也随机分布于这个区间中。而实际上我们只需要得到该区间的左端即可。处理方法很简单:假设我们得到的临时答案是K(K∈[X,X + min(a,b,c))),那么K - min(K%a,K%b,K%c) = X.也就是只需要把临时答案减去其与a、b、c三者中取余的最小值即可!
        return (int)(left - Math.min(Math.min(left % a,left % b),left % c));
    }
    // 最小公倍数
    private long lcm(long a, long b) {
        // 最小公倍数等于两个数相乘除以最大公约数
        return a * b / gcd(a, b);
    }
    // 最大公约数
    private long gcd (long a, long b) {
        if (a == 0) return b;
        return gcd(b % a, a);
    }
}
上一篇:[Ynoi2019 模拟赛] Yuno loves sqrt technology III


下一篇:全国大学生数学建模大赛2021卷III答案解析