SRM540-div2-3-FractionInDifferentBases


题目大意:
     在正整数区间[A,B]内有多少个满足一下条件的的正整数X:给定正整数P和Q,P/Q所表示的有理数用X进制表示是一个无限循环小数。
     数据规模:P为[0,10^12], Q为 [1, 10^12],  A和B均为[2,10^18], A<=B 

思路:
     该问题的解题关键是观察到以下数学性质:如果P和Q互质,那么有理数P/Q在X进制下可以表示成有限小数的充要条件是X拥有Q的所有质因子。
     根据该性质,我们首先需要令Q=Q/gcd(Q,P),使得有理数的分数形式最简化。然后计算Q的所有质因子,X拥有Q所有质因子等价于X是这些质因子乘积的倍数。令所有质因子的乘积为product,那么A,B之间为product倍数的X都会使得P/Q表示成有限小数,相反的其他所有数都使得P/Q为无限循环小数。整个算法的计算瓶颈在于计算Q的质因子上,下面例程中该部分的计算复杂度为O(sqrt(Q)),在题目给定的数据规模下没有问题。

证明:如果P和Q互质,那么有理数P/Q在X进制下可以表示成有限小数的充要条件是X拥有Q的所有质因子。
充分性:
     假设P/Q所表示成的有限小数的小数部分为: 
     d1/X + d2/X^2 + d2/X^3 + ... + dk/X^k
     则P/Q乘以X^k之后将变成整数,即P*(X^k)/Q为整数。由于P和Q互质,那么X^k为Q的倍数,X显然必须拥有Q的所有质因子。
必要性:
     假设存在一个拥有Q所有质因子的X使得P/Q不是有限小数(显然Q>=2),那么必然是无限循环小数,假设小数部分为:
          d1 ... di d(i+1) ... d(i+k)  d (i+1)  ... d (i+k)   d (i+1)  ... d (i+k)  ...
即i+1到i+k之间是循环体,那么当n为非负整数时所有有理数(X^(i+k*n))*P/Q都具有相同的小数部分(循环体部分)。分别取n为0和足够大的整数N,则(X^(i+k*N))×P/Q-(X^i)*P/Q为整数,即(X^i)*(X^(k*N)-1)*P/Q为整数。显然(X^i)不会是Q的倍数,那么另Q'=Q/gcd(Q, X^i ),则得到以下结论:
          X^(k*N)-1是Q'的倍数
由于X也拥有Q'的所有质因子,当N足够大时有以下结论:
          X^(k*N)是Q'的倍数
根据以上两个结论有1是Q'的倍数,显然这是不可能的。所以假设不成立,原命题成立。


Java代码:
public class FractionInDifferentBases {
    private long getFactorProduct(long Q) {
        long product = 1;
        for (long i = 2; i * i <= Q; ++i) {
            boolean ok = false ;
            while (Q % i == 0) {
                Q /= i;
                ok = true;
            }
            if(ok){
                product *= i;
            }
        }
        if(Q != 1){
            product *= Q;
        }
        return product;
    }
   
    private long gcd(long a, long b){
        return b == 0 ? a : gcd(b, a % b);
    }
    public long getNumberOfGoodBases(long P, long Q, long A, long B) {
        Q /= gcd(P, Q);
        if (Q == 1) {
            return 0;
        }
        long product = getFactorProduct(Q);
        long total = B - A + 1;
        if(A % product != 0){
            A = A + product - A % product;
        }
        long bad = B >= A ? (B - A) / product + 1 : 0;
        return total - bad;
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值