题解 | #小红的循环节长度#

小红的循环节长度

https://ac.nowcoder.com/acm/contest/62622/E

参考:如何找到一个循环小数循环节?

欧拉定理

维基百科|欧拉定理

简单说就是,如果aabb为正整数,且互为质数也就是gcd(a,b)=1gcd(a, b) = 1,那么:

aΦ(b)1(mod b)a^{\Phi(b)} \equiv 1(mod\ b)

该式表示aΦ(b)a^{\Phi(b)}与1在模bb下同余,即aΦ(b)  %  b=1a^{\Phi(b)} \; \% \; b = 1 ,其中Φ(b)\Phi(b)为欧拉函数。

欧拉函数

Φ(n)\Phi(n)表示小于等于nn的所有正整数中与nn互素的数的个数。显然如果我们O(n)O(n)的复杂度去遍历求解欧拉函数是不行的,它有一个更快的公式:

如果n=ipiαin = \prod_i p_i^{\alpha_i},其中pip_i为质数,该式对于nn表示的是质因数分解,那么:

Φ(n)=ni(11pi)\Phi(n) = n\prod_i(1-\frac{1}{p_i})

简单证明一下,记nn以内某个质数pip_i的倍数的集合为Si=npiS_i = \lfloor \frac{n}{p_i} \rfloor,目标就是找到它们的并集的大小SS,并将nn减去SS,就是Φ(n)\Phi(n)

根据集合论,其实S=SiSi×Sj+...+(1)n1S1×...×SnS = \sum S_i - \sum S_i\times S_j + ... + (-1)^{n-1}S_1 \times ... \times S_n,奇数相加、偶数相减。

如何找到pq\frac{p}{q}的循环节?

考虑1q\frac{1}{q}

  1. 如果qq的因子仅含有2或5,那么该小数是循环小数;
  2. 如果不含2或5,10与qq互素,根据欧拉定理,10Φ(q)1(mod  q)10^{\Phi(q)} \equiv 1(mod\; q),其中Φ(q)\Phi(q)便是循环节,但不一定是最小的,因为最小的循环节重复k遍仍是循环节,所以通过遍历Φ(q)\Phi(q)的因子找到最小的循环节即可。
  3. 如果除了2和5还有其他的,先找出2和5的个数分别为a与b,循环节前面一段的长度便是max(a,b)max(a, b),剩下的10与q2a5b\frac{q}{2^a5^b}互素,找到最小循环节即可。

import math
def phi(x: int) -> int:
    ans, i = x, 2
    while i * i <= x:
        if x % i == 0:
            ans = ans // i * (i - 1)
            while x % i == 0: x //= i 
        i += 1
    if x > 1: ans = ans // x * (x - 1)
    return ans

p, q = map(int, input().split())
g = math.gcd(p, q)
p //= g
q //= g # 化简
c2, c5 = 0, 0
while q % 2 == 0:
    q //= 2
    c2 += 1
while q % 5 == 0:
    q //= 5
    c5 += 1
if q == 1:
    print(-1)
    exit(0)
phiq = phi(q) # 拿到欧拉函数的值
ans, i = phiq, 2 # 找到最小的循环节
while i * i <= phiq:
    if phiq % i == 0: # 是因子
        if pow(10, i, q) == 1: ans = min(ans, i)
        if pow(10, phiq // i, q) == 1: ans = min(ans, phiq // i)
    i += 1
print(max(c2, c5), ans)

PS:涉及大整数的乘法、求模还是用python吧,用C++开到__int128才行。

全部评论

相关推荐

3 收藏 评论
分享
牛客网
牛客企业服务