题目来源:POJ 3358
题目链接:http://poj.org/problem?id=3358
题目大意:求二进制小数循环节的起点和长度
解题思路:将小数转换成分数更易求解。以1/10为例:1/10, 2/10, 4/10, 8/10 16/10,32/10,64/10 …… 取模后为1/10,2/10,4/10,8/10,6/10,2/10,4/10……由此可以看出循环节为4,循环起点为2。
算法实现:
#include<cstdio>
#include<vector>
using namespace std;
#define Max 9223372036854775807ULL
long long a[1000005];
vector<int> b;
void prime()
{
int i,j;
b.clear();
for (i=2; i<1000005; i++)
{
if (!a[i])
{
b.push_back(i); //全部的素数
for (j=i*2; j<1000005; j=j+i)
a[j]=1;
}
}
}//a[素数]=0,其余为1
long long euler(long long n)
{
long long i,res=n;
if (n==1)
return 0;
for (i=0; i<b.size(); i++)
{
if (n%b[i]==0)
{
res=res/b[i]*(b[i]-1);
while (n%b[i]==0)
n=n/b[i];
if (n==1)
break;
}
}
if (n!=1)
res=res/n*(n-1);
return res;
}
long long gcd(long long p,long long q)
{
if (p<q)
return gcd(q,p);
else if (p%q==0)
return q;
else
return gcd(q,p%q);
}
long long js(int x,long long q)
{
long long i=0,res=1,curr=2%q;
while (x>0)
{
if (x&1)
res=(res*curr)%q;
i++;
x>>=1;
curr=(curr*curr)%q;
}
return res;
}
long long deal(long long t,long long q)
{
int i,j;
long long mmin=Max;
for (i=1; i*i<=t; i++)
{
if (t%i==0)
{
if (js(i,q)==1)
return i;
j=t/i;
if (js(j,q)==1 && j<mmin)
mmin=j;
}
}
return mmin;
}
int main()
{
long long p,q,i,t,x,cas=0;
prime();
while (scanf("%lld/%lld",&p,&q)!=EOF)
{
cas++;
x=0;
if (p==0)//分子为0,则从小数点后一位循环,最小周期为1
{
printf("Case #%lld: 1,1\n",cas);
continue;
}
long long y=gcd(p,q);
p=p/y;
q=q/y;
long long q1=q;
while (q1%2==0)
{
x++;
q1=q1/2;
}
x++;
t=euler(q1);
if (t!=0)
t=deal(t,q1);
printf("Case #%lld: %lld,%lld\n",cas,x,t);
}
return 0;
}