問題
奇素数 $p$ について、原始根判定 / 離散対数 / 位数計算の3種クエリに答えよ。
制約
$3 \le p \le 10^9$
$Q \le 10^5$
$1 \le g, a, b < p$
入出力例
入力例 1
7 4
1 3
2 3 5
3 2
1 2出力例 1
Yes
5
3
Noヒント (段階的開示)
ヒント1: 方向性
原始根判定は位数 = $p-1$ チェック。離散対数は BSGS で $O(\sqrt{p})$。位数は $p-1$ の素因数分解 + 試し。
ヒント2: アプローチ
$\mathrm{ord}_p(a)$ は $p-1$ の約数。$p-1$ の各素因数 $q$ で割り続けて確認。
ヒント3: BSGS
$a^x = b$ を $a^{im} = b a^{-j}$ と分解、$m = \lceil\sqrt{p}\rceil$。
模範解答 (Python)
import sys
from math import isqrt
input = sys.stdin.readline
def factorize(n):
factors = {}
d = 2
while d * d <= n:
while n % d == 0:
factors[d] = factors.get(d, 0) + 1
n //= d
d += 1
if n > 1:
factors[n] = factors.get(n, 0) + 1
return factors
def multiplicative_order(a, p, factors_pm1):
ord_val = p - 1
for q in factors_pm1:
while ord_val % q == 0 and pow(a, ord_val // q, p) == 1:
ord_val //= q
return ord_val
def bsgs(a, b, p):
if b == 1:
return 0
m = isqrt(p - 1) + 1
table = {}
aj = 1
for j in range(m):
if aj not in table:
table[aj] = j
aj = aj * a % p
am_inv = pow(pow(a, m, p), p - 2, p)
cur = b
for i in range(m + 1):
if cur in table:
x = i * m + table[cur]
return x
cur = cur * am_inv % p
return -1
def solve():
line = input().split()
p, Q = int(line[0]), int(line[1])
factors_pm1 = factorize(p - 1)
results = []
for _ in range(Q):
query = list(map(int, input().split()))
qtype = query[0]
if qtype == 1:
g = query[1]
if g % p == 0:
results.append("No"); continue
ord_g = multiplicative_order(g, p, factors_pm1)
results.append("Yes" if ord_g == p - 1 else "No")
elif qtype == 2:
a, b = query[1], query[2]
x = bsgs(a, b, p)
results.append(str(x))
elif qtype == 3:
a = query[1]
if a % p == 0:
results.append("0"); continue
ord_a = multiplicative_order(a, p, factors_pm1)
results.append(str(ord_a))
print('\n'.join(results))
solve()
Step-by-Step 解説
1$p-1$ の素因数分解
$\sqrt{p}$ までの試し割り。
$\sqrt{p}$ までの試し割り。
2位数計算
各素因数 $q$ で
各素因数 $q$ で
ord_val // q 乗が 1 の間は割り続ける。3原始根判定
$\mathrm{ord}_p(g) = p - 1$ なら原始根。
$\mathrm{ord}_p(g) = p - 1$ なら原始根。
4BSGS
baby step: $a^j$ をハッシュテーブル。giant step: $b \cdot (a^{-m})^i$ を検索。
baby step: $a^j$ をハッシュテーブル。giant step: $b \cdot (a^{-m})^i$ を検索。
5クエリ処理
$p-1$ の素因数分解は一度だけ。
$p-1$ の素因数分解は一度だけ。
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
| BSGS で x=0 見落とし | b=1 のケース | 先頭で if b==1: return 0 |
| 位数で素因数の指数を無視 | $q^2$ で割れる場合 | while ord_val % q == 0 ループ |
| 逆元計算忘れ | $a^{-m}$ が必要 | pow(pow(a,m,p), p-2, p) |
| BSGS の m が小さい | 解見落とし | m = isqrt(p-1) + 1 |
次のステップ
- Pohlig-Hellman アルゴリズム
- 楕円曲線上の離散対数(ECDLP)