題意:輸入一個有理數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;}