POJ_3358 Period of an Infinite Binary Expansion

来源:互联网 发布:同性网络剧2017 编辑:程序博客网 时间:2024/05/14 18:49

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

题意:

给你一个有理分数,要你求这个分数写成2进制小数时的最小循环节和最开始的位置

思路:

一开始以为可以直接暴力模拟搞,因为没有给p、q的范围,写了一个暴力的代码,一

交RE了,更了几次还是RE,说明算法不对。 后来就直接用数论的方法去做了,具体

的思路是这样的:假设分数为p/q,先p/=gcd(p,q) ,q/=gcd(p,q),因为是找循环节,所

以只要找一个i和j使得: p*2^i = p*2^j ( mod q ),(我们这里假设i < j),因此上式又可

以转化为:p*2^i-p*2^j = 0( mod q ), p*2^i*(1-2^(j-i)) = 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是一定不会有2的因子的 ,所有q中2的因子应该都是由2^i来消除,假设

剩余的q为q1, 因此我们又可以得到下面的式子:q1 | ( 2 ^(j-i) - 1) ,令j-i = x , 这样我们

就可以转化2^x = 1(mod q1), 这是一点高阶同余方程,有经典的求法。我们可以利用

欧拉定理来做,欧拉定理告诉我们:a^phi(p) = 1( mod p )当且仅当 gcd(a,n) = 1时成立。

这样,在我们的式子中,gcd(2 , q1) = 1 ,所以我们只需要求出q1的欧拉函数值就可以

了,但是还要要求我们所求的x最小,这样我们就可以枚举r | phi(q1) ,求出最小的x。

代码:

#include<stdio.h>#include<string.h>typedef long long LL ;LL p ,q ;char ch[1000000] ;LL ans1 , ans2 ;LL gcd(LL a , LL b){    while(b){        LL c = a ;        a = b ;        b = c % b ;    }    return a ;}LL cal_phi(LL n){    LL res = n ;    for(LL i=2;i*i<=n;i++){        if( n%i == 0 ){           n /= i;           for( ;n%i==0;n/=i) ;           res = res/i*(i-1) ;        }    }    if(n > 1)   res = res/n*(n-1) ;    return res ;}bool is_ok(LL a ,LL b , LL c){    LL res = 1 , add = a ;    while(b){        if(b & 1){            res = res * add % c ;        }        add = add * add % c ;        b /= 2 ;    }    return res == 1;}LL min(LL a , LL b){    return a > b ? b : a ;}void solve(LL q){    LL phiq = cal_phi(q) ;    ans2 = phiq ;    for(LL r=1;r*r<=phiq;r++){        if(phiq % r != 0)   continue ;        if( is_ok(2,r,q) ){            ans2 = min(ans2, r) ;        }        if( is_ok(2,phiq/r,q) ){            ans2 = min( ans2 , phiq / r );        }    }}void calc(){    int i , j ;    j = 0 ;    LL res = q ;    while(res % 2 == 0 ){        j++ ;        res /= 2 ;    }    ans1 = j + 1 ;    if(res == 1){        ans2 = 1 ;    }    else        solve( res );    printf("%lld,%lld\n",ans1,ans2);}int main(){    int i ,cas=0 ;    while(scanf("%s",ch) == 1){        ++cas ;        int len = strlen(ch);        p = q = 0 ;        for(i=0;i<len;i++){            if(ch[i] == '/')    break ;            p = p * 10 + ch[i] - '0' ;        }        for(i++;i<len;i++){            q = q * 10 + ch[i] - '0' ;        }        LL g = gcd(p,q) ;        p /= g ; q /= g ;        printf("Case #%d: ",cas);        calc() ;    }    return 0;}
 

原创粉丝点击