POJ 3358 Period of an Infinite Binary Expansion 欧拉函数+欧拉定理

题意:输入一个有理数p/q(保证是一个小数),然后将其小数部分用二进制表示。求出在此种表示下的循环起点和循环节长度

{x} = 0.a1a2...ar(ar+1ar+2...ar+s)w

题解:来自Discuss

我们可以观察一下1/10这组数据,按照二进制转换法(乘二法),我们可以得到:
1/10  2/10 4/10 8/10 16/10 32/10 ...
然后都分子都尽可能减去10,得到:
1/10  2/10 4/10 8/10 6/10 2/10 ...
这时候,发现出现了重复,那么这个重复就是我们要求的最小循环。
抽象出模型如下:对p/q
首先p'=p/gcd(p,q)
q'=q/gcd(p,q);

然后我们就是求p'*2^i == p'*2^j (mod q')   (“==”表示同余,i<j)
经过变换得到:
p'*2^i*(2^(j-i)-1) ==0 (mod q')
也就是 q' | p'*2^i*(2^(j-i)-1)
由于gcd(p',q')=1,
得到: q' | 2^i*(2^(j-i)-1)
因为2^(j-i)-1为奇数,所以q'有多少个2的幂,i就是多少,而且i就是循环开始位置的前一位。
那么令q''为q'除去2的幂之后的数
此时 q'' | 2^(j-i)-1
也就是求出x,使得 2^x ==1 (mod q'')    

欧拉定理:若a与p互质,则a^Φ(p) == 1 (modp)

所以2^x ==1 (mod q'')   必定存在解。

现在我要求的是最小循环节。又已知,若a,p互质,且a^x == 1 (mod p), 那么必定有a | Φ(p)。

至此,只需要从小到大枚举Φ(p)的因子即可。

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

__int64 fact[100000], pf;


__int64 Euler ( __int64 n )
{
    __int64 ret = n, i;
    for ( i = 2; i * i <= n; i++ )
    {
        if ( n % i == 0 )
        {
            n /= i;
            ret = ret - ret / i;
            while ( n % i == 0 )
                n /= i;
        }
    }
    if ( n > 1 )
        ret = ret - ret / n;
    return ret;
}

__int64 gcd ( __int64 a, __int64 b )
{
    __int64 c;
    while ( b != 0 )
    {
        c = a % b;
        a = b;
        b = c;
    }
    return a;
}

__int64 mod_mult ( __int64 a, __int64 b, __int64 n )
{
    __int64 ret = 0;
    a = a % n;
    while ( b >= 1 )
    {
        if ( b & 1 )
        {
            ret += a;
            if ( ret >= n ) ret -= n;
        }
        a <<= 1;
        if ( a >= n ) a -= n;
        b >>= 1;
    }
    return ret;
}

__int64 mod_exp ( __int64 a, __int64 b, __int64 n )
{
    __int64 ret = 1;
    a = a % n;
    while ( b >= 1 )
    {
        if ( b & 1 )
            ret = mod_mult(ret,a,n);
        a = mod_mult(a,a,n);
        b >>= 1;
    }
    return ret;
}

void factor ( __int64 x )
{
    pf = 0;
    for ( __int64 i = 2; i * i <= x; i++ )
        if ( x % i == 0 )
            fact[pf++] = i, fact[pf++] = x / i;
}

int main()
{
    __int64 eul;
    __int64 p, q, d, cnt, cs = 0;
    while ( scanf("%I64d/%I64d",&p,&q) != EOF )
    {
        if(!p){ printf("Case #%I64d: 1,1 \n",++cs);continue; }
        d = gcd(p,q);
        p /= d; q /= d;
        cnt = 1;
        while ( q % 2 == 0 ) { q >>= 1; cnt++; }

        eul = Euler(q);
        factor(eul);
        sort(fact,fact+pf);
        fact[pf++] = eul; __int64 i;
        for ( i = 0; i < pf; i++ )
            if ( mod_exp (2, fact[i], q) == 1 ) break;
        printf("Case #%I64d: %I64d,%I64d\n",++cs,cnt,fact[i]);
    }
    return 0;
}


 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值