POJ3358 Period of an Infinite Binary Expansion 欧拉定理应用

题目链接:http://poj.org/problem?id=3358


题目大意:给出一个小于1的分数,把他转换成二进制形式,并找出小数点到循环部分的最少距离以及循环节的最小长度。


分析:对于一个分数n/m,首先我们要把它化成最简形式:分别令n/=gcd(n,m),m/=gcd(n,m)。至于将一个十进制小数化为k进制小数的方法呢,我们有如下代码段(数组bit用来存放对应k进制的小数部分。):

while(true)
{
    n=n*k;
    bit[i++]=n/m;
    n=n%m;
}

当得到我们所需的精度时跳出循环即可。可以知道,对于某一位的n,设此时为ni=n * 2^i,当到了某一位nj=n * 2^j满足ni mod m=nj mod m时,那么就说明小数部分出现了循环,循环节的长度为j-i。

    抽象出模型如下:对于最简分数p/q,我们有p * 2^i≡p * 2^j(mod q),变换得到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^i之后的数,此时有q' | ( 2^(j-i) -1 ),也就是求出x,使得2^x≡1(mod q').

    由于q'和2互素,所以(由欧拉定理:2^Φ(q')≡1(mod q') )此方程必有解,且Φ(q')为方程的一个解。但不一定是最小解。这里和POJ3696有点相似,可知最小解为Φ(q')的一个因子。

    解题步骤如下:

   (1)求解x=Φ(q');

   (2)找出Φ(q')的所有素因子pi;

   (3)令x=x/pi,直到2^x≠1(mod m)或pi不能整除x。如果2^x≠1(mod m),令x=x×pi;

   (4)重复步骤(3),直到所有的素因子都经过(3)处理;

   (5)此时x就是满足2^x≡1(mod m)的最小解。


实现代码如下:

#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
int p,q;
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
int phi(int n)
{
    int rea=n;
    for(int i=2;i*i<=n;i++)
      if(n%i==0)
      {
          rea=rea-rea/i;
          do n/=i;
          while(n%i==0);
      }
    if(n>1) rea=rea-rea/n;
    return rea;
}
int fac[50][2],ct;
void get_fac(int n)
{
    ct=0;
    for(int i=2;i*i<=n;i++)
      if(n%i==0)
      {
          fac[ct][0]=i;
          fac[ct][1]=0;
          do
          {
              fac[ct][1]++;
              n/=i;
          }while(n%i==0);
          ct++;
      }
    if(n>1)
    {
        fac[ct][0]=n;
        fac[ct++][1]=1;
    }
}
ll quick_mod(ll x,ll m)
{
    ll ans=1,a=2;
    while(x)
    {
        if(x&1) ans=ans*a%m;
        a=a*a%m;
        x>>=1;
    }
    return ans;
}
int main()
{
    int T=1;
    while(scanf("%d/%d",&p,&q)!=-1)
    {
        printf("Case #%d: ",T++);
        if(!p)
        {
            puts("1,1");
            continue;
        }
        int gd=gcd(p,q);
        p/=gd,q/=gd;
        int ans1=1;
        while(!(q&1))
        {
            q>>=1;
            ans1++;
        }
        int ans2=phi(q);
        get_fac(ans2);
        for(int i=0;i<ct;i++)
          for(int j=1;j<=fac[i][1];j++)
            if(quick_mod((ll)ans2/fac[i][0],(ll)q)==1)
              ans2/=fac[i][0];
        printf("%d,%d\n",ans1,ans2);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值