1. 程式人生 > >理科男 C組模擬賽

理科男 C組模擬賽

題目大意:

對於給定的分數 A / B,求其在 K 進位制下是有限小數還是迴圈小數。如果是有限小數,求小數點後的位數;如果是迴圈小數,則求混迴圈部分和迴圈節的長度又分別是多少。
注意,迴圈節指的是最短迴圈節,且混迴圈部分的長度也指最短。

解題思路+證明:

看到正解時我驚呆了~什麼鬼?!?!
(數論蒟蒻表示 我的f**k!玩什麼飛機)
這個世界的題目長這樣嗎?
於是。。讓dalao教我教了半個小時——
這個東西太難證,於是把題解拷了下來

神奇的題解+證明

首先把 A / B 約分成既約分數。設 a[1] = A,r[n] 為原分數小數點後第 n 位的數。
顯然有 r[1] = floor(K * a[1] / B)。
剩下來的餘數 a[2] = K * a[1] mod B。
依此類推我們有 r[n] = floor(K * a[n] / B),a[n] = K * a[n - 1] mod B。
不難發現如果 a[p] = a[q](p < q),那麼小數點後第 p 位到第 q - 1 位這一段就可以視為一
個迴圈節。
暴力計算數列 a,找到第一個與前面重複的項,就可以找到最短迴圈節了。
這個重複的項前面的部分匯出混迴圈部分。
如果最早在 p 處計算到 a[p] = 0,那麼原分數就是一個小數點後有 p - 1 位的有限小數。
以上便是 50 分的解法。
本題前 50 分屬於送分。如果沒能送到你的手上,建議你去參加 noip 普及組的比賽。
下面我們對 a 數列的性質做一些討論。
如果 (B, K) = 1,對於任意的 i 都有 (a[i], B) = 1。
設 K’ 為 K 模 B 時的乘法逆元,即 KK’ mod B = 1。由乘法逆元的性質 K’ 存在且唯一。
假設最早出現重複的位置是 a[p] = a[q] (p < q)。
如果 p != 1,那麼 a[p - 1] = K’ * a[p] mod B = K’ * a[q] mod B = a[q - 1]。
也就是出現了更早的重複,與題設矛盾。所以顯然有 p = 1。
這時,顯然原分數是一個純迴圈小數,且最短迴圈節長度是 q - 1。
設 x = q - 1。顯然 a[q] = a[1] * K^x mod B = a[1],於是 K^x mod B = 1。
這就轉化成了求 K 模 B 的階的問題了。
由尤拉定理 K^phi(B) = 1 (mod B),由階的性質 x | phi(B)。
我們可以將 phi(B) 分解素因數,並初始化 x = phi(B)。
之後考慮 phi(B) 的每個素因數 p。如果 K^(x/p) = 1 (mod B),就 x ← x / p,並繼續試除 p。
否則轉下一個素因數。這樣就可以求出 K 模 B 的階了,這就是最短迴圈節的長度。
如果 (B, K) > 1,那麼 (a[2], B) > 1。設 (a[2], B) = g,不難發現對於任意的 i ≥ 2,有 g | (a[i],
B)。
不妨設 B’ = B / g,a’[i] = a[i] / g (i ≥ 2)。
若此時 (B’, K) = 1,就轉化為了上面的情況。否則繼續這個過程。
如果上面的轉換進行了 T 次,由於 a[1] 到 a[T] 與後面 a 數列的迴圈無關。
卡一下範圍便會知道迴圈節的最後一個數字與混迴圈部分最後一個數字一定不相等。
於是原分數的混迴圈長度就是 T 了。
特殊地,如果在 T 次轉換之後得到的最後一個 B = 1,那麼之後 a 數列的值全為 0。這時
我們可以斷言原分數是一個小數點後有 T 位的有限小數。
以上便是 100 分的做法。
如果感覺“想出這種做法簡直就是不可能的事情嘛”的話,請努力學習初等數論的內容。
這些推導的難度實際上是相當低的。

源程式:

#include<cstdio>
#include<cctype>
#include<cstring>
#define LL long long
using namespace std;
int t,ans1;
LL f,A,B,K,d;
LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
inline LL read()
{
    LL d=0,f=1;char c=getchar();
    while (!isdigit(c)){if (c=='-')f=-1;c=getchar();}
    while
(isdigit(c)){d=d*10+(int)c-48;c=getchar();} return f*d; } void write(LL x) {if(x>9) write(x/10);putchar(x%10+48);return;} LL QG(LL x) { LL y=x; for(int i=2;(LL)i*i<=x;i++) if(x%i==0) {y=y/i*(i-1);do x/=i;while(x%i==0);} if(x>1) y=y/x*(x-1); return y; } LL GQG(LL a,LL b,LL p) { LL c=0
; for(;b;b>>=1,a=(a<<1)%p) if(b&1) c=(c+a)%p; return c; } LL ZQG(LL a,LL b,LL p) { LL ans=1; for(;b;b>>=1,a=GQG(a,a,p)) if(b&1) ans=GQG(ans,a,p); return ans; } LL Sum(LL A,LL B) { LL x=QG(B),y=x; for(int i=2;(LL)i*i<=x;i++) if(x%i==0) { while(!(y%i)&&ZQG(A,y/i,B)==1)y/=i; do x/=i;while(x%i==0); } if(x>1&&ZQG(A,y/x,B)==1)y/=x; return y; } int main() { t=read(); for (int fxxk=1;fxxk<=t;fxxk++) { A=read();B=read();K=read(); d=gcd(A,B);A/=d;B/=d; ans1=0; while(1) { d=gcd(B,K); if(d==1) break; B/=d; ans1++; } write(ans1);putchar(32); if(B==1) putchar(48); else write(Sum(K,B)); putchar(10); } return 0; }

為數不多的解題報告後序

看來我要多學點數論方面的知識—不然以後看到這樣的題目不得完蛋?!?!