题目链接: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;
}