【数论】poj3358 Period of an Infinite Binary Expansion

传送门:戳我


大致题意:给你一个分数,将其转化为2进制小数,输出小数开始循环的位置和循环节长度。


人比较菜……没有独立做出来……推到下面1式的时候就推不动了。

果然数论什么的烧智商Orz

思路和代码都是参考别人的……


解题思路:首先,我们要知道如何把一个分数化为2进制的小数

假设分数是n/m。

首先,化简分式,n=n/gcd(n,m),m=m/gcd(n,m),n=n% m

然后,一个分式化为2进制小数方法:

While (需要的位数){

    n=n*k;

    bit[i++]=n/m;

    n=n%m;

}

我们可以发现,对于某一位ni,nj,当ni % m==nj % m 时出现循环节,循环节长度为L=j-i。

继续分析,Nj=ni*2^L,则ni % m == ni*2^L % m  -->  2^L %m ==1……(1)

联想到欧拉定理a^phi(p)%p==1,

我们可以得出:当2和m互质的时候,(1)式满足欧拉定理,也就是说,循环节从第1位就开始出现。

当2和m不互质的时候呢?为了把(1)式向欧拉定理靠,我们可以同时除以2^t,得2^(L-t)%(m*2^-t)==1,分析这个式子,可以发现小数满足上式则开始循环,也就是说循环是从t+1位开始的。

接下来的工作就是求出满足2^L’ % m’==1的L’和m’了,对比欧拉定理a^phi(p)%p==1,那么显然只需要找出一个合适的p,问题就解决了。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
 
 
int t, n, m, GCD, phi, ans1, ans2;
int temp, num;
int fac[1000000];
 
int gcd(int x, int y){
    int r;
    if(x < y){
        swap(x, y);
    }
    while(x%y){
        r = x%y;
        x = y;
        y = r;
    }
    return y;
}
 
int euler(int x){
    int y = (int)sqrt(x+0.5);
    int curans = x;
    for(int i = 2;i <= y;i++){
        if(x%i == 0){
            curans = curans/i*(i-1);
            while(x%i == 0){
                x /= i;
            }
        }
    }
    if(x > 1){
        curans = curans/x*(x-1);
    }
    return curans;
}
 
int quickpow(int m, int n, int k){
    int b = 1;
    while(n > 0){
        if(n&1){
            b = (long long)b*m%k;
        }
        n >>= 1;
        m = (long long)m*m%k;
    }
    return b;
}
 
int main(){
    int T = 0;
    while(scanf("%d/%d", &n, &m)!=EOF){
        T++;
        GCD = gcd(n, m);

        n /= GCD;
        m /= GCD;

        t = 0;
        while(m%2 == 0){
            t++;
            m /= 2;
        }
        ans1 = t+1;
        phi = euler(m);
        if(phi == 1){
            ans2 == 1;
        }
        else{
            num = 0;
            for(int i = 1;i*i <= phi;i++){
                if(phi%i == 0)
                {
                    fac[num++] = i;
                    fac[num++] = phi/i;
                }
            }

            sort(fac, fac+num);

            for(int i = 0;i < num;i++){
                temp = quickpow(2, fac[i], m);

                if(temp == 1){
                    ans2 = fac[i];
                    break;
                }
            }
        }
        printf("Case #%d: ", T);
        printf("%d,%d\n", ans1, ans2);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值