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?猛戳)。
最后,附上丑陋的代码......
#include <iostream> #include <cstdio> typedef long long ll; using namespace std; ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; } ll quick_multiply(ll a,ll b,ll mod) { ll ans=; a%=mod; b%=mod; while(b) { ) ans=(ans+a)%mod; b>>=; a=(a+a)%mod; } return ans; } ll quick_power(ll a,ll b,ll mod) { ll ans=; a%=mod; while(b) { ) ans=quick_multiply(ans,a,mod); b>>=; a=quick_multiply(a,a,mod); } return ans; } int main() { ll a,b; ll cnt=; while(~scanf("%I64d/%I64d",&a,&b)) { ll k=a,r=b; a/=gcd(k,r); b/=gcd(k,r); ll sum=; ==) b/=,sum++; printf(); ll phi=b; ll m=b; ;i*i<=m;++i) { ) { phi=phi/i*(i-); ) { m/=i; } } } ) phi=phi/m*(m-); ll minn=phi; ;i*i<=phi;++i) { ) { ,i,b)==) minn=min(minn,i); ,phi/i,b)==) minn=min(minn,phi/i); } } printf("%I64d\n",minn); } }
小结,我们发现,这题对于a的取值并没有什么要求,证明是显然的。