传送门:戳我
大致题意:给你一个分数,将其转化为2进制小数,输出小数开始循环的位置和循环节长度。
人比较菜……没有独立做出来……推到下面1式的时候就推不动了。
果然数论什么的烧智商Orz
思路和代码都是参考别人的……
解题思路:首先,我们要知道如何把一个分数化为2进制的小数
假设分数是n/m。
首先,化简分式,n=n/gcd(n,m),m=m/gcd(n,m),n=n% m
然后,一个分式化为2进制小数方法:
While (需要的位数){
n=n*k;
bit[i++]=n/m;
n=n%m;
}
我们可以发现,对于某一位ni,nj,当ni % m==nj % m 时出现循环节,循环节长度为L=j-i。
继续分析,Nj=ni*2^L,则ni % m == ni*2^L % m --> 2^L %m ==1……(1)
联想到欧拉定理a^phi(p)%p==1,
我们可以得出:当2和m互质的时候,(1)式满足欧拉定理,也就是说,循环节从第1位就开始出现。
当2和m不互质的时候呢?为了把(1)式向欧拉定理靠,我们可以同时除以2^t,得2^(L-t)%(m*2^-t)==1,分析这个式子,可以发现小数满足上式则开始循环,也就是说循环是从t+1位开始的。
接下来的工作就是求出满足2^L’ % m’==1的L’和m’了,对比欧拉定理a^phi(p)%p==1,那么显然只需要找出一个合适的p,问题就解决了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
int t, n, m, GCD, phi, ans1, ans2;
int temp, num;
int fac[1000000];
int gcd(int x, int y){
int r;
if(x < y){
swap(x, y);
}
while(x%y){
r = x%y;
x = y;
y = r;
}
return y;
}
int euler(int x){
int y = (int)sqrt(x+0.5);
int curans = x;
for(int i = 2;i <= y;i++){
if(x%i == 0){
curans = curans/i*(i-1);
while(x%i == 0){
x /= i;
}
}
}
if(x > 1){
curans = curans/x*(x-1);
}
return curans;
}
int quickpow(int m, int n, int k){
int b = 1;
while(n > 0){
if(n&1){
b = (long long)b*m%k;
}
n >>= 1;
m = (long long)m*m%k;
}
return b;
}
int main(){
int T = 0;
while(scanf("%d/%d", &n, &m)!=EOF){
T++;
GCD = gcd(n, m);
n /= GCD;
m /= GCD;
t = 0;
while(m%2 == 0){
t++;
m /= 2;
}
ans1 = t+1;
phi = euler(m);
if(phi == 1){
ans2 == 1;
}
else{
num = 0;
for(int i = 1;i*i <= phi;i++){
if(phi%i == 0)
{
fac[num++] = i;
fac[num++] = phi/i;
}
}
sort(fac, fac+num);
for(int i = 0;i < num;i++){
temp = quickpow(2, fac[i], m);
if(temp == 1){
ans2 = fac[i];
break;
}
}
}
printf("Case #%d: ", T);
printf("%d,%d\n", ans1, ans2);
}
return 0;
}