POJ-3358Period of an Infinite Binary Expansion

题目:

http://poj.org/problem?id=3358

题意:

给出一个分数,求该分数的小数部分循环节起点与长度。

思路:

相当神奇的一道题,真真是毫无思路。

挣扎无果之后看网上的解题报告也是混乱,后来看到一个写得比较清楚的终于豁然开朗:

http://blog.csdn.net/tsaid/article/details/7304212

简单地说就是利用欧拉定理

//欧拉定理:a与p互质,a^x==1(mod p)则x==euler[p];
具体解法是,首先取gcd(n,m)化为真分数,

当出现循环节时有:n*2^i==n*2^j(mod m),消去n,可以变换为:2^i*(2^(j-i)-1)==0(mod m).

因为(2^(j-i)-1)是奇数,所以取m对于2的幂就是i,也就求出了循环节起点。

之后再利用欧拉定理(j-i)==eulur[m],取eulur[m]所有因子枚举找出最小循环节就行了。

代码:

long long n,m;
long long flag,ave,ans,res,len,ans1,ans2;

long long modMul(long long a,long long b,long long n)
{
    long long res = 0;
    while(b)
    {
        if(b&1)    res = (res + a) % n;
        a = (a + a) % n;
        b >>= 1;
    }
    return res;
}
long long modExp(long long x,long long k,long long mod)
{
    long long ans = 1;
    while(k)
    {
        if(k & 1) ans = modMul(ans, x, mod);
        x = modMul(x, x, mod);
        k >>= 1;
    }
    return ans;
}
long long factor[1000];
int sum;

long long gcd(long long a,long long b)
{
    if(a==0)return 1;
    if(a<0) return gcd(-a,b);
    while(b)
    {
        long long t=a%b;
        a=b;
        b=t;
    }
    return a;
}
void fact( long long x )
{
    sum = 0;
    for ( long long i = 2; i * i <= x; i++ )
        if ( x % i == 0 )
            factor[sum++] = i, factor[sum++] = x / i;
}
long long euler(long long n)
{
     long long res=n,a=n;
     for(long long i=2;i*i<=a;i++)
         if(a%i==0)
         {
             res=res/i*(i-1);
             while(a%i==0) a/=i;
         }
     if(a>1) res=res/a*(a-1);
     return res;
}
int main()
{
    long long i,j,k,kk,t,x,y,z;
    kk=0;
    while(scanf("%lld/%lld",&n,&m)!=EOF)
    {
        if(!n)
        {
            printf("Case #%lld: 1,1\n",++kk);
            continue;
        }
        t=gcd(n,m);
        n/=t;m/=t;
        res=1;
        while(m%2==0)m/=2,res++;

        ans=euler(m);
        fact(ans);
        sort(factor,factor+sum);
        factor[sum++]=ans;
        for(i=0;i<sum;i++)
            if(modExp(2,factor[i],m)==1)
                break;
        printf("Case #%lld: %lld,%lld\n",++kk,res,factor[i]);
    }
    return 0;
}









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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值