<<  < 2013 - >  >>
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31




今天做数论专题,碰到一个很有趣的题目。
题目的大意为给你一个分数p/q,如121/1472这样的分数。
题目要你求出小数点后面循环开始的位置i以及循环节的长度。
刚开始看题的时候就懵了,虽然高中以前做过这种进制转化,以及变小数的题目,但是老衲真心不记得了。今天这个题目还是真的挺有用的。
言归正传,根据题目的意思,很容易让人想到的是用朴素的方法每次乘以2然后取余(我没有试过,不知道这样暴力能不能过),但是这种方法显然不是最优的办法呢。
下面就用比较正式一点的数论知识解决这个问题吧。
首先对于p和q我们显然可以先求出他们的最大公约数d,并分别用d去除p和q。
这样得到了新的p和q。(还可以p=p%q,这样保证了p<q)
第二步就是进行一定的化简了哦。
首先对于任何整数,我们都可以表示为q=r*(2^m),显然这是没有问题的。
对于分子可以分解为 p=r*t+x(这里的r是分母分解出来的那个r)
首先不看2^m,先看(r*t+x)/r=t+x/r,这里t为一个整数,所以对于这个数,它在小数点的前面呢,不用管。同时这里x和r也是互素的(想想为什么?)于是循环节的长度就是x/r在二进制中的循环的长度。不妨设(x*2^a)=(x*2^b)(mod r),这样就等价于2^|a-b|=1(mod r),于是看到这个东西肯定就瞬间明白了一点什么了呢。对没错,循环的长度就是euler(r)。但是我实践了一下,答案不对,为什么会不对呢?惊奇地,我发现这里长度答案是标准的一个倍数。所以这里的答案可能是euler(r)的某一个约数呢。所以我们要枚举它的约数这样才能确定最短的循环长度(其实题目里面有提示哦)。
对于循环开始的位置,那就更加容易了。上面说到(r*t+x)/r为一个整数,于是就看下面的2^m这里相当于小数点左移了m位,加上原来应该有的一位是m+1位。这样一来这个问题就完美解决了。(参考自某个不知名的神牛)
极力推荐对数论有兴趣的同学去做做这个题目。
下面附上我的代码:


#i nclude <iostream>
#i nclude <cstdio>
#define ll long long
using namespace std;

ll gcd(ll a,ll b)
{
    return b==0?a:gcd(b,a%b);
}

ll power(ll a,ll b,ll M)
{
    ll ans=1;
    while (b>0)
    {
        if (b&1) ans=(ans*a)%M;
        b>>=1;
        a=a*a;
        if (a>M) a%=M;
    }
    return ans;
}

ll phi(int x)
{
    ll ans=x;
    for (int i=2; i*i<=x; i++)
    {
        if (x%i==0)
        {
            ans=ans/i*(i-1);
            while (x%i==0) x/=i;
        }
    }
    if (x>1) ans=ans/x*(x-1);
    return ans;
}

ll count(ll a,ll b)
{
    ll ans;
    for (ll i=1; i*i<=a; i++)
    {
        if (a%i!=0) continue;
        if (power(2,i,b)==1) return i;
        if (power(2,a/i,b)==1) ans=a/i;
    }
    return ans;
}

int main()
{
    ll k,p,q,pos,cas=0;
    while (scanf("%I64d/%I64d",&p,&q)!=EOF)
    {
        if (p==0)
        {
            printf("Case #%I64d: 1,1 \n",++cas);
            continue;
        }
        k=gcd(p,q);
        p/=k,q/=k,p%=q;
        k=p,pos=1;
        while (!(q&1)) q>>=1,pos++;
        k=phi(q);
        k=count(k,q);
        printf("Case #%I64d: %I64d,%I64d \n",++cas,pos,k);
    }
    return 0;
}

(*^__^*) 嘻嘻……
  • 标签:数论 
  • 发表评论:
    天涯博客欢迎您!