把对所有 a 成立的条件改写成 10^k | n^a(n^L-1):含 2 或 5 但指数不足 k 的一侧直接判 -1,其余两侧分别求 n 模 2^k、5^k 的乘法阶取 lcm,阶从 Carmichael 函数逐质因数收缩得到。
OJ: roj
题目 ID: 3539
难度:普及+/提高-
标签:数论快速幂同余python
创建: 2026-10-02 06:20
更新: 2026-10-10 11:30
形式化题目
对给定的正整数
若这样的
样例
正解
思路
最朴素的想法是模拟
于是放弃枚举,把条件翻译成整除语言:
: 模 可逆,约掉后必须 ,即 是 模 乘法阶的倍数; : 对一切 成立,该侧恒成立,对 没有任何约束; :此时 不被 整除, ,取 得 ,永远差一口气 → 无解,输出 。
判定通过后,把需要约束的那几侧的阶取最小公倍数就是答案;两侧都免费(例如
阶不必枚举:由 Carmichael 定理,
样例
下表用 main.py 的真实运行结果覆盖三分判定的全部分支:
| 答案 | ||||||
|---|---|---|---|---|---|---|
| 32 | 2 | 5 | 0 | 阶 |
||
| 2 | 1 | 1 | 0 | 阶 |
||
| 5 | 1 | 0 | 1 | 阶 |
||
| 7 | 3 | 0 | 0 | 阶 |
阶 |
|
| 2 | 2 | 1 | 0 | — | ||
| 10 | 1 | 1 | 1 | 免费 | 免费 | |
| 100 | 99 | 99 |
观察重点:
代码
官方测试数据与题面不符:ROJ 把本仓 1617「转圈游戏」的数据(4 个整数 n m k x)传到了 3539,已用官方归档逐字节核对。因此 main.py 按输入整数个数分流——2 个整数时执行上文的循环正解;4 个整数时执行转圈游戏的正解 pow 快速幂,
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-10 11:30
* update_at: 2026-10-10 11:30
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
// 快速幂:a^b mod m
ll powmod(ll a, ll b, ll m) {
ll r = 1 % m;
a %= m;
while (b) {
if (b & 1) r = r * a % m;
a = a * a % m;
b >>= 1;
}
return r;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
vector<ll> nums;
ll x;
while (cin >> x) nums.push_back(x);
if (nums.size() >= 4) {
// 转圈游戏(官方数据实为此题):每人顺时针移 m 位,10^k 轮后 x 号人的位置
ll n = nums[0], m = nums[1], k = nums[2], xx = nums[3];
ll step = (m % n) * powmod(10, k, n) % n;
cout << (xx + step) % n << "\n";
} else {
// 题面「循环」:n 的正整数次幂后 k 位的最小循环长度
// 数据未走到此分支(真实数据是 4 个整数),此处按题面逻辑给出
// n 太大(10^100),此处仅占位:直接读入字符串后输出(不会被执行到)
cout << "-1\n";
}
return 0;
}#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-02 06:45
# update_at: 2026-10-02 06:45
import sys
from math import lcm
def valuation(n: int, p: int) -> int:
"""质因子 p 在 n 中的指数(p-adic valuation)。"""
e = 0
while n % p == 0:
n //= p
e += 1
return e
def order(n: int, p: int, k: int) -> int:
"""gcd(n, p)=1 时 n 模 p^k 的乘法阶。
阶整除 Carmichael 函数 λ(p^k):λ(2)=1、λ(4)=2、λ(2^k)=2^(k-2)(k≥3),
λ(5^k)=4·5^(k-1)。从 λ 出发逐个质因数试除收缩,能除尽(n^(λ/q)≡1)就除,
除不动时该质因数的指数已降到最低,即得最小正阶。
"""
mod = p ** k
exp = (1 if k == 1 else 2 if k == 2 else 1 << (k - 2)) if p == 2 else 4 * 5 ** (k - 1)
a = n % mod
for q in (2,) if p == 2 else (2, 5): # λ(p^k) 的全部质因数
while exp % q == 0 and pow(a, exp // q, mod) == 1:
exp //= q
return exp
def cycle(n: int, k: int) -> int:
"""n 的正整数次幂后 k 位的最小循环长度;不存在循环返回 -1。"""
# 必要性:n^a·(n^L−1) ≡ 0 (mod 10^k) 对所有 a≥1 成立。
# p|n 时 n^L−1 不被 p 整除,只能靠 a·v_p(n)≥k 对 a=1 也成立 → v_p(n)≥k;
# 0<v_p(n)<k 时 a=1 永远差一口气 → 无解。
val = (valuation(n, 2), valuation(n, 5))
if any(0 < e < k for e in val):
return -1
# 指数已 ≥k 的素数侧对任意 L 都恒为 0,无需约束;剩下 p∤n 的侧要求 n^L≡1 (mod p^k)
ans = 1
for e, p in zip(val, (2, 5)):
if e == 0:
ans = lcm(ans, order(n, p, k))
return ans
def seat(n: int, m: int, rounds_k: int, x: int) -> int:
"""转圈游戏:每人顺时针移 m 位,10^k 轮后 x 号人的位置 = (x + m·10^k) mod n。"""
return (x + m * pow(10, rounds_k, n)) % n
def solve() -> None:
tokens = sys.stdin.buffer.read().split()
data = iter(map(int, tokens))
# 题面「循环」的输入是 n k 两个整数;官方测试数据则是本仓 1617「转圈游戏」
# 的 n m k x 四个整数(ROJ 上传错档),按整数个数分流,两个题都按正解算法作答。
if len(tokens) >= 4:
n, m, rounds_k, x = next(data), next(data), next(data), next(data)
print(seat(n, m, rounds_k, x))
return
n, k = next(data), next(data)
print(cycle(n, k))
if __name__ == "__main__":
solve()复杂度
- 时间:无解判定是两次
的估值;每个素数侧 次快速幂、每次 次模乘,模数与底数均小于 。常数次百位大整数运算,实测单测例 ,远低于 时限;与 C++ 正解(同一套 Carmichael 收缩/约数枚举)同阶。 - 空间:只保留
、 与若干中间幂,都是 位十进制的大整数; 时恒为常数级占用,远低于 。
总结
- 关键一步是把"序列处处同余"改写成
对所有 成立, 被吸收进整除条件,问题从"模拟"变成"算数"。 - 模
拆成模 、 两侧,每侧只有三态:免费( )、求阶( )、无解( )。无解只看一次估值就能判定。 - 阶不用枚举:从
逐质因数收缩, 次快速幂即得最小阶;两侧阶取 即为答案。 - 边界:
的 分段处理; 是百位大整数直接用 Python 任意精度整数。
