目录:
题目:
分析:
面我们对 a 数列的性质做一些讨论。
如果
(B,K)=1
(
B
,
K
)
=
1
,对于任意的 i 都有
(a[i],B)=1
(
a
[
i
]
,
B
)
=
1
。
设 K’ 为 K 模 B 时的乘法逆元,即
KK′modB=1
K
K
′
m
o
d
B
=
1
。由乘法逆元的性质 K’ 存在且唯一。
假设最早出现重复的位置是
a[p]=a[q](p<q)
a
[
p
]
=
a
[
q
]
(
p
<
q
)
。
如果
p!=1
p
!
=
1
,那么
a[p−1]=K′∗a[p]modB=K′∗a[q]modB=a[q−1]
a
[
p
−
1
]
=
K
′
∗
a
[
p
]
m
o
d
B
=
K
′
∗
a
[
q
]
m
o
d
B
=
a
[
q
−
1
]
。
也就是出现了更早的重复,与题设矛盾。所以显然有 p = 1。
这时,显然原分数是一个纯循环小数,且最短循环节长度是 q - 1。
设 x = q - 1。显然
a[q]=a[1]∗KxmodB=a[1]
a
[
q
]
=
a
[
1
]
∗
K
x
m
o
d
B
=
a
[
1
]
,于是
KxmodB=1
K
x
m
o
d
B
=
1
。
这就转化成了求 K 模 B 的阶的问题了。
由欧拉定理
Kphi(B)=1(modB)
K
p
h
i
(
B
)
=
1
(
m
o
d
B
)
,由阶的性质
x|φ(B)
x
|
φ
(
B
)
。
我们可以将
phi(B)
p
h
i
(
B
)
分解素因数,并初始化
x=φ(B)
x
=
φ
(
B
)
。
之后考虑 phi(B) 的每个素因数 p。如果
K(x/p)=1(modB)
K
(
x
/
p
)
=
1
(
m
o
d
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
B
′
=
B
/
g
,
a′[i]=a[i]/g(i≥2)
a
′
[
i
]
=
a
[
i
]
/
g
(
i
≥
2
)
。
若此时 (B’, K) = 1,就转化为了上面的情况。否则继续这个过程。
如果上面的转换进行了 T 次,由于 a[1] 到 a[T] 与后面 a 数列的循环无关。
卡一下范围便会知道循环节的最后一个数字与混循环部分最后一个数字一定不相等。
于是原分数的混循环长度就是 T 了。
特殊地,如果在 T 次转换之后得到的最后一个 B = 1,那么之后 a 数列的值全为 0。这时
我们可以断言原分数是一个小数点后有 T 位的有限小数。
代码:
#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;int t,ans1;
LL gcd(LL a,LL b){return b?gcd(b,a%b):a;}
char c;
LL f,A,B,K;
inline LL read()
{
f=0;
while(c=getchar(),c<=47||c>=58);f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),c>=48&&c<=57) f=(f<<3)+(f<<1)+c-48;
return f;
}
void write(LL x){if(x>9) write(x/10);putchar(x%10+48);return;}
LL phl(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 mul(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 ksm(LL a,LL b,LL p)//快速幂 拆分指数
{
LL ans=1;
for(;b;b>>=1,a=mul(a,a,p)) if(b&1) ans=mul(ans,a,p);
return ans;
}
LL cal(LL A,LL B)
{
LL x=phl(B),y=x;
for(int i=2;(LL)i*i<=x;++i)
if(x%i==0)//约分
{
while(!(y%i)&&ksm(A,y/i,B)==1)y/=i;
do x/=i;while(x%i==0);
}
if(x>1&&ksm(A,y/x,B)==1)y/=x;//求循环节
return y;
}
int main()
{
t=read();
while(t--)
{
A=read();B=read();K=read();
LL 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(cal(K,B));
putchar(10);
}
return 0;
}