1. 程式人生 > >bzoj 3031: 理科男

bzoj 3031: 理科男

std scrip con cst 包含 測試數據 amp 分別是 col

Description

  吃過草莓刨冰之後,Vani和cl有些疲倦地坐在一個長椅上。
  “吶,玩得開心嗎?”Vani忽然問道。
  “嗯……很,很開心的說。”
  “那麽,我有一個問題想要問你呢。”
  cl的臉有點紅了起來。
  “嗯……好吧。問、問吧……我會告訴你的哦……”
  “那好。對於一個分數A / B……”
  “嗯……哎?哎?!”


  “……就是這個問題。我覺得這個問題好糾結啊……”
  Vani淡定地說完這句話。
  “啊?!哈啊?!”

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

Input

  第一行一個正整數 T,表示測試數據的數目。
  每個測試數據包含三個空格分隔的整數 A, B, K。含義如題目所示。

Output

  對於每個測試數據,在單獨的一行內輸出兩個空格分隔的整數 M, R。
  其中 M 表示混循環部分的長度,R 表示循環節的長度。
  如果 A / B 在 K 進制下是有限小數,則 R = 0,M 為小數點後面的位數;如果 A / B 在 K 進制下是純循環小數,則 M = 0。

$minimize\space M,R\space s.t.$ $\frac{A}{B}=\frac{x}{(K^R-1)K^M}$ 將$\frac{A}{B}$化為最簡分數後,答案只和$B,K$有關 若$gcd(B,K)>1$則有混循環部分,將$B$不斷除以$gcd(B,K)$直到互質,除的次數即為$M$
R即為求$\frac{B}{gcd(B,K^M)}$在模K意義下與K互質的數構成的乘法群中的階,答案是$\phi(K)$的約數,可以試除得到
#include<cstdio>
typedef long long i64;
i64 gcd(i64 a,i64 b){
    for(i64 c;b;c=a,a=b,b=c%b);
    return a;
}
i64 phi(i64 x){
    i64 y=x;
    for(int i=2;i64(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;
}
i64 mul(i64 a,i64 b,i64 p){
    i64 c=0;
    for(;b;b>>=1,a=(a<<1)%p)if(b&1)c=(c+a)%p;
    return c;
}
i64 pw(i64 a,i64 n,i64 p){
    i64 v=1;
    for(;n;n>>=1,a=mul(a,a,p))if(n&1)v=mul(v,a,p);
    return v;
}
i64 cal(i64 A,i64 B){
    i64 x=phi(B),y=x;
    for(int i=2;i64(i)*i<=x;++i)if(x%i==0){
        while(y%i==0&&pw(A,y/i,B)==1)y/=i;
        do x/=i;while(x%i==0);
    }
    if(x>1&&pw(A,y/x,B)==1)y/=x;
    return y;
}
int main(){
    int T;
    for(scanf("%d",&T);T;--T){
        i64 a,b,c;
        scanf("%lld%lld%lld",&a,&b,&c);
        i64 g=gcd(a,b);
        a/=g,b/=g;
        int a1=0;
        while(1){
            i64 x=gcd(b,c);
            if(x==1)break;
            b/=x;
            ++a1;
        }
        printf("%d %lld\n",a1,b==1?0ll:cal(c,b));
    }
    return 0;
}

bzoj 3031: 理科男