poj 3358 Period of an Infinite Binary Expansion——欧拉函数+费马小定理
题目意思是给定m,n问你 m/n的二进制表示的最大循环节开始位置和长度。
找了一些题解,发现没有满意的,discuss里面的分析已经很好了。。
我们可以观察一下1/10这组数据,按照二进制转换法(乘二法),我们可以得到:
1/10 2/10 4/10 8/10 16/10 32/10 ...
然后都分子都尽可能减去10,得到:
1/10 2/10 4/10 8/10 6/10 2/10 ...
这时候,发现出现了重复,那么这个重复就是我们要求的最小循环。
抽象出模型如下:对p/q
首先p'=p/gcd(p,q)
q'=q/gcd(p,q);
然后我们就是求p'*2^i == p'*2^j (mod q') (“==”表示同余,i<j;
经过变换得到:
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的幂之后的数
此时 q'' | 2^(j-i)-1
也就是求出x,使得 2^x ==1 (mod q'')
因此我们还要枚举phi(q)的因子,判断有 2 ^ x == 1(mod q) ,取最小的x(PS:先开始这个地方写搓了,很容易错的呀。。)
a27400 | 3358 | Accepted | 400K | 0MS | G++ | 1258B | 2011-09-06 18:57:36 |
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
typedef long long LL;
LL gcd(LL a,LL b)
{
if(b==0)
return a;
else return gcd(b,a%b);
}
LL euler(LL n)
{
LL i;
LL total=n;
for(i=2;i*i<=n;i++)
{
if(n%i==0)
{
total=total/i*(i-1);
while(n%i==0) n/=i;
}
}
if(n!=1)
total=total/n*(n-1);
return total;
}
LL quickmod(LL a,LL n,LL p)
{
if(n==0)
return 1%p;
if(n==1)
return a%p;
a%=p;
LL ans=quickmod(a,n/2,p)%p;
ans=ans*ans%p;
if(n&1)
ans=ans*a%p;
return ans;
}
int main(void)
{
LL p,q;
LL cas=0;
char ch;
while(scanf("%lld%c%lld",&p,&ch,&q)==3)
{
LL temp=gcd(p,q);
p=p/temp;
q=q/temp;
LL i=1,j=-1;
while(q%2==0)
{
q/=2;
i++;
}
LL phi=euler(q);
LL k;
for(k=2;k*k<=phi;k++)
{
if(phi%k==0)
{
if(quickmod(2,k,q)==1)
{
if(j==-1)
j=k;
else if(j>k)
j=k;
break;
}
else
{
LL temp=phi/k;//先开始忘记写else这块了。。。
if(quickmod(2,temp,q)==1)
{
if(j==-1)
j=temp;
else if(j>temp)
j=temp;
}
}
}
}
if(j==-1)
j=phi;
printf("Case #%lld: %lld,%lld\n",++cas,i,j);
}
return 0;
}