题目链接

循环节

题目描述

求分数 表示为循环小数时,其循环节的长度。注意:可以用有限数表示的数的循环节的长度为1。

解题思路

本题要求计算分数 的循环节长度,且 的范围很大(最高可达 ),因此直接模拟长除法会超时。正确的解法需要运用数论知识。

1. 问题转化与数论基础

分数 的小数表示的循环节长度,本质上是求满足 的最小正整数

这里的 是将原分母 中所有与基数 共享的质因子(即 )都除去后得到的新分母。这是因为因子 只会影响小数的不循环部分,而循环部分完全由与 互质的因子 决定。

2. 处理有限小数

我们首先计算 :不断将 除以 直到它不再能被这两者整除。

  • 如果最终得到的 等于 ,说明 的所有质因子仅包含 。这意味着 是一个有限小数。
  • 根据题目的特殊规定:“可以用有限数表示的数的循环节的长度为1”,此时我们应输出

3. 求解循环节长度(乘法阶)

如果 ,问题就变成了求 的乘法阶(Order)。

  • 根据欧拉定理,我们知道 ,其中 是欧拉函数,表示小于 且与 互质的正整数的个数。
  • 这个定理告诉我们,我们要求的最小的 一定是 的一个因子。
  • 因此,我们的算法步骤如下: a. 计算 。 b. 计算欧拉函数值 phi_N_prime = 。 c. 找出 phi_N_prime 的所有因子。 d. 将这些因子从小到大排序。 e. 遍历每一个因子 ,使用快速幂算法检查是否 。 f. 第一个满足该条件的因子 就是我们要求的最小循环节长度。

这个方法将问题从模拟计算转变为数论函数的计算,效率足以通过本题。

代码

#include <bits/stdc++.h>

using namespace std;

long long power(long long base, long long exp, long long mod) {
    long long res = 1;
    base %= mod;
    while (exp > 0) {
        if (exp % 2 == 1) res = (__int128)res * base % mod;
        base = (__int128)base * base % mod;
        exp /= 2;
    }
    return res;
}

long long phi(long long n) {
    long long result = n;
    for (long long i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            while (n % i == 0)
                n /= i;
            result -= result / i;
        }
    }
    if (n > 1)
        result -= result / n;
    return result;
}

void solve() {
    long long n;
    cin >> n;
    while (n % 2 == 0) n /= 2;
    while (n % 5 == 0) n /= 5;

    if (n == 1) {
        cout << 1 << endl;
        return;
    }

    long long euler_phi = phi(n);
    vector<long long> divisors;
    for (long long i = 1; i * i <= euler_phi; ++i) {
        if (euler_phi % i == 0) {
            divisors.push_back(i);
            if (i * i != euler_phi) {
                divisors.push_back(euler_phi / i);
            }
        }
    }
    sort(divisors.begin(), divisors.end());

    for (long long d : divisors) {
        if (power(10, d, n) == 1) {
            cout << d << endl;
            return;
        }
    }
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int t = sc.nextInt();
        while (t-- > 0) {
            solve(sc);
        }
    }

    public static void solve(Scanner sc) {
        long n = sc.nextLong();
        while (n % 2 == 0) n /= 2;
        while (n % 5 == 0) n /= 5;

        if (n == 1) {
            System.out.println(1);
            return;
        }

        long eulerPhi = phi(n);
        List<Long> divisors = getDivisors(eulerPhi);
        Collections.sort(divisors);

        for (long d : divisors) {
            if (power(10, d, n) == 1) {
                System.out.println(d);
                return;
            }
        }
    }

    private static long phi(long n) {
        long result = n;
        for (long i = 2; i * i <= n; i++) {
            if (n % i == 0) {
                while (n % i == 0)
                    n /= i;
                result -= result / i;
            }
        }
        if (n > 1)
            result -= result / n;
        return result;
    }
    
    private static List<Long> getDivisors(long n) {
        List<Long> divisors = new ArrayList<>();
        for (long i = 1; i * i <= n; i++) {
            if (n % i == 0) {
                divisors.add(i);
                if (i * i != n) {
                    divisors.add(n / i);
                }
            }
        }
        return divisors;
    }

    private static long power(long base, long exp, long mod) {
        long res = 1;
        base %= mod;
        while (exp > 0) {
            if (exp % 2 == 1) res = (res * base) % mod;
            base = (base * base) % mod;
            exp /= 2;
        }
        return res;
    }
}
import sys

def phi(n):
    result = n
    p = 2
    while p * p <= n:
        if n % p == 0:
            while n % p == 0:
                n //= p
            result -= result // p
        p += 1
    if n > 1:
        result -= result // n
    return result

def get_divisors(n):
    divs = []
    for i in range(1, int(n**0.5) + 1):
        if n % i == 0:
            divs.append(i)
            if i*i != n:
                divs.append(n//i)
    divs.sort()
    return divs

def power(base, exp, mod):
    res = 1
    base %= mod
    while exp > 0:
        if exp % 2 == 1:
            res = (res * base) % mod
        base = (base * base) % mod
        exp //= 2
    return res

def solve():
    n_str = sys.stdin.readline()
    if not n_str: return
    n = int(n_str)

    while n % 2 == 0:
        n //= 2
    while n % 5 == 0:
        n //= 5

    if n == 1:
        print(1)
        return

    euler_phi = phi(n)
    divisors = get_divisors(euler_phi)

    for d in divisors:
        if power(10, d, n) == 1:
            print(d)
            return

def main():
    t_str = sys.stdin.readline()
    if not t_str: return
    t = int(t_str)
    for _ in range(t):
        solve()

if __name__ == "__main__":
    main()

算法及复杂度

  • 算法:数论(欧拉定理、乘法阶)
  • 时间复杂度:。主要开销来自于计算欧拉函数 和寻找 的因子,这两者的复杂度都是根号级别的。之后遍历因子并进行快速幂的计算,其复杂度远小于前者。
  • 空间复杂度:,其中 的因子数量,这个值增长非常缓慢,远小于