Period of an Infinite Binary Expansion
题目大意:给你一个分数,求这个分数二进制表示下从第几位开始循环,并求出最小循环节长度。
注释:int范围内。
想法:这题说实话,是一道神题!我们思考一下,如何将一个小数换成二进制?连续的乘2,然后取首位。这样的比较简洁的转换方式注定了这题其实是可做的。我们可以抓住循环节的在这样的方法下是怎样形成的?显然,存在一个x,使得每多乘$2^x$都会使得所形成的答案相同。第二点,我们知道,一个分数的循环节开始之前是不参与循环的。这样,我们思考,多次反复的乘以2,它的整数部分modb是相同的对吧!?!我们在此就有一个式子,就是说
$2^i\\cdot a\\equiv 2^j\\cdot a(mod\\ b)$。其中j>i
可以变形为$2^j\\cdot a-2^i\\cdot a\\equiv 0(mod\\ b)$
$\\Rightarrow 2^i\\cdot a\\cdot(2^{j-i}-1)$首先,我们在此之前不妨现将a,b弄成互质。现在,gcd(a,b)=1,所以,我们有
$2^i\\cdot (2^{j-i}-1)\\equiv 0(mod\\ b)$
显然,我们想求最小值,我们发现,如果i的值增加,循环节的长度是不会改变的,所以,我们必须求出最小的且满足题意的i,而这个i,就是b中2的个数。剩下的,就是另一道题了。参考博主博客The Luckiest Number(The Luckiest Number?猛戳)。
最后,附上丑陋的代码......
1 #include <iostream> 2 #include <cstdio> 3 typedef long long ll; 4 using namespace std; 5 ll gcd(ll a,ll b) 6 { 7 return b?gcd(b,a%b):a; 8 } 9 ll quick_multiply(ll a,ll b,ll mod) 10 { 11 ll ans=0; 12 a%=mod; 13 b%=mod; 14 while(b) 15 { 16 if(b&1) ans=(ans+a)%mod; 17 b>>=1; 18 a=(a+a)%mod; 19 } 20 return ans; 21 } 22 ll quick_power(ll a,ll b,ll mod) 23 { 24 ll ans=1; 25 a%=mod; 26 while(b) 27 { 28 if(b&1) ans=quick_multiply(ans,a,mod); 29 b>>=1; 30 a=quick_multiply(a,a,mod); 31 } 32 return ans; 33 } 34 int main() 35 { 36 ll a,b; 37 ll cnt=0; 38 while(~scanf("%I64d/%I64d",&a,&b)) 39 { 40 ll k=a,r=b; 41 a/=gcd(k,r); 42 b/=gcd(k,r); 43 ll sum=0; 44 while(b%2==0) b/=2,sum++; 45 printf("Case #%I64d: %I64d,",++cnt,sum+1); 46 ll phi=b; 47 ll m=b; 48 for(int i=2;i*i<=m;++i) 49 { 50 if(m%i==0) 51 { 52 phi=phi/i*(i-1); 53 while(m%i==0) 54 { 55 m/=i; 56 } 57 } 58 } 59 if(m!=1) phi=phi/m*(m-1); 60 ll minn=phi; 61 for(ll i=1;i*i<=phi;++i) 62 { 63 if(phi%i==0) 64 { 65 if(quick_power(2,i,b)==1) minn=min(minn,i); 66 if(quick_power(2,phi/i,b)==1) minn=min(minn,phi/i); 67 } 68 } 69 printf("%I64d\\n",minn); 70 } 71 }
小结,我们发现,这题对于a的取值并没有什么要求,证明是显然的。