题意:输入一个有理数p/q(保证是一个小数),然后将其小数部分用二进制表示。求出在此种表示下的循环起点和循环节长度
{x} = 0.a1a2...ar(ar+1ar+2...ar+s)w
题解:来自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'')
欧拉定理:若a与p互质,则a^Φ(p) == 1 (modp)
所以2^x ==1 (mod q'') 必定存在解。
现在我要求的是最小循环节。又已知,若a,p互质,且a^x == 1 (mod p), 那么必定有a | Φ(p)。
至此,只需要从小到大枚举Φ(p)的因子即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
__int64 fact[100000], pf;
__int64 Euler ( __int64 n )
{
__int64 ret = n, i;
for ( i = 2; i * i <= n; i++ )
{
if ( n % i == 0 )
{
n /= i;
ret = ret - ret / i;
while ( n % i == 0 )
n /= i;
}
}
if ( n > 1 )
ret = ret - ret / n;
return ret;
}
__int64 gcd ( __int64 a, __int64 b )
{
__int64 c;
while ( b != 0 )
{
c = a % b;
a = b;
b = c;
}
return a;
}
__int64 mod_mult ( __int64 a, __int64 b, __int64 n )
{
__int64 ret = 0;
a = a % n;
while ( b >= 1 )
{
if ( b & 1 )
{
ret += a;
if ( ret >= n ) ret -= n;
}
a <<= 1;
if ( a >= n ) a -= n;
b >>= 1;
}
return ret;
}
__int64 mod_exp ( __int64 a, __int64 b, __int64 n )
{
__int64 ret = 1;
a = a % n;
while ( b >= 1 )
{
if ( b & 1 )
ret = mod_mult(ret,a,n);
a = mod_mult(a,a,n);
b >>= 1;
}
return ret;
}
void factor ( __int64 x )
{
pf = 0;
for ( __int64 i = 2; i * i <= x; i++ )
if ( x % i == 0 )
fact[pf++] = i, fact[pf++] = x / i;
}
int main()
{
__int64 eul;
__int64 p, q, d, cnt, cs = 0;
while ( scanf("%I64d/%I64d",&p,&q) != EOF )
{
if(!p){ printf("Case #%I64d: 1,1 \n",++cs);continue; }
d = gcd(p,q);
p /= d; q /= d;
cnt = 1;
while ( q % 2 == 0 ) { q >>= 1; cnt++; }
eul = Euler(q);
factor(eul);
sort(fact,fact+pf);
fact[pf++] = eul; __int64 i;
for ( i = 0; i < pf; i++ )
if ( mod_exp (2, fact[i], q) == 1 ) break;
printf("Case #%I64d: %I64d,%I64d\n",++cs,cnt,fact[i]);
}
return 0;
}