POJ-3358-Period of an Infinite Binary Expansion 解题报告

        欧拉定理,同余运算性质,好题。费了好大劲才看题解懂得差不多,还是有一点点地方想不出来,如果有谁知道请告诉我,谢谢。题意:给你两个整数p和q,请输出p/q作为小数在二进制表示下的第一个循环节在第几位小数和最小循环节长度。比如1/10的二进制小数表示为0.00011001100110011......那么1/10的最小循环节是0011,长度为4,它第一次出现是在第二位小数上。


       我的解题思路:首先要把p/q转化成为最简真分数,我们还是用p和q表示,也就是说化简后p<q且p与q互质或者p为0。接下来就要转化成同余问题了。

二进制思考可能比较难懂,我们假设是十进制的情况下,p/q = 0.123333333......这样的情况,那么循环节是3,长度为1。我们可以得到这样一个式子p × 10^2 = p × 10^3 (mod q),为什么?很明显p × 10^2 / q = 12.3333333......,p × 10^3 / q = 123.3333333......,这两个数后面的小数部分相同,小数部分都是余数除以除数的产物,因此之前的那个式子显然成立,这就好像1/3和4/30的小数部分相同一样。

为什么是p乘10的次方而不是别的次方呢?这是因为我们讨论的是十进制的情况下,如果是二进制的情况下那又是乘2的次方了。为什么是p乘10的2次方而不是1次方什么的呢?因为我们要保证小数部分相同,否则p × 10^1 / q = 1.2333......了。

到了这里好办了,我们要把非循环节的小数部分都转移到整数部分,假设非循环节小数部分长度为x,最小循环节长度为y。那么很明显这么一个同余式就成立了p × 10^x = p × 10^(x+y) (mod q),题目就是要求找到x和最小的y。

现在把十进制问题转换成二进制,那么现在同余式就变成了p × 2^x = p × 2^(x+y) (mod q),我们假设z = x + y,因为p与q互质,根据同余式的性质化简为2^x = 2^z (mod q),然后得到2^z - 2^x = 0 (mod q)即2^x × (2^y - 1) = 0 (mod q),也就是说q能整除2^x × (2^y - 1),由于2^y - 1是奇数,好,现在注意了,因此q能被2整除多少次,x就是多少。(我也没想通为什么,谁要是知道,请告诉我,不胜感激)

假设q` = q / 2^x,那么根据同余式的除法原理,2^x × (2^y - 1) = 0 (mod q`),因为q`与2互质,因此可将原式化简为2^y = 1( mod q`)。到了这里,就可以用欧拉定理来解题了,题目要求求最小的y而q`与2恰好互质,因此根据欧拉定理的推广满足此式最小的y必定能整除phi(q`),我们只需要枚举q`的约数并判断是否满足此式就行了。由于本题描述并不是很清晰,最后要注意的地方是当p等于0时输出的结果。


       我的解题代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>

using namespace std;

typedef long long Long;

const int N = 100000;

Long p, q;
Long ans[N], ansn;  //枚举的q`的约数

void InitRead();

void DataProcess();

Long Phi(Long x);

Long FastPow(Long base, Long n, Long mod);

Long Gcd(Long a, Long b)
{
    return b == 0 ? a : Gcd(b, a % b);
}

int main()
{
    while (~scanf("%lld/%lld", &p, &q))
    {
        InitRead();
        DataProcess();
    }
    return 0;
}

void InitRead()
{
    Long gcd = Gcd(p, q);
    p /= gcd;
    q /= gcd;
    p %= q;
    ansn = 0;
    return;
}

void DataProcess()
{
    static int tn = 1;
    if (p == 0)
    {
        printf("Case #%d: %lld,%lld\n", tn++, 1LL, 1LL);
        return;
    }
    Long x = 0;
    while (q % 2 == 0)
    {
        q /= 2;
        x++;
    }
    Long phi_q = Phi(q);
    Long cnt = (Long)sqrt(phi_q + 0.5) + 1;
    for (Long i=1; i<cnt; ++i)
    {
        if (phi_q % i == 0)
        {
            if (FastPow(2, i, q) == 1) ans[ansn++] = i;
            if (FastPow(2, phi_q / i, q) == 1) ans[ansn++] = phi_q / i;
        }
    }
    sort(ans, ans+ansn);
    printf("Case #%d: %lld,%lld\n", tn++, x+1, ans[0]);
    return;
}

Long FastPow(Long base, Long n, Long mod)
{
    Long ans = 1;
    base %= mod;
    while (n)
    {
        if (n & 1)
        {
            ans *= base;
            ans %= mod;
        }
        base *= base;
        base %= mod;
        n >>= 1;
    }
    return ans;
}

Long Phi(Long x)
{
    Long ans = x;
    Long cnt = (Long)sqrt(x + 0.5) + 1;
    for (Long i=2; i<cnt; ++i)
    {
        if (x % i == 0)
        {
            ans -= ans / i;
            while (x % i == 0) x /= i;
        }
    }
    if (x > 1) ans -= ans / x;
    return ans;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值