Day 128-Q3 — 離散対数問題(Baby-step Giant-step法)

2026-08-20 赤色 Master / Phase 8+ ★★★★★★★★★ baby-step前計算とgiant-step逆元探索でmod pの離散対数をO(√p)で解く

問題

素数 $P$ と整数 $A,B$($1\le A,B-1を出力せよ。

入力形式

A B P

制約

$P$ は素数、$3 \le P \le 10^{12}$
$1 \le A, B < P$
$A \not\equiv 0 \pmod P$

入出力例

入力例1

2 11 13

出力例1

7

2^7=128≡11 (mod13)。x=0..6では11にならず、x=7が最小。

入力例2

2 5 7

出力例2

-1

2^x mod 7は1,2,4の巡回を繰り返し、5には決してならない。

ヒント(段階的開示)

ヒント1(方向性)

$x$を0から順に試すと最悪$O(P)$で$P\le10^{12}$では間に合わない。$x$を「大きい桁」と「小さい桁」に分け、両方をそれぞれ$O(\sqrt P)$個だけ調べるBaby-step Giant-step法(BSGS)を使う。

ヒント2(アプローチ)

$m=\lceil\sqrt P\rceil$として$x=im+j$($0\le i\le m,0\le j

ヒント3(誘導)
m = math.isqrt(p) + 1
baby = {}
cur = 1
for j in range(m):
    if cur not in baby:
        baby[cur] = j
    cur = cur * a % p

a_m_inv = pow(pow(a, m, p), p - 2, p)   # (A^m)^{-1} mod p

gamma = b
for i in range(m + 1):
    if gamma in baby:
        answer = i * m + baby[gamma]
        break
    gamma = gamma * a_m_inv % p
else:
    answer = -1

模範解答 (Python)

import sys
import math


def solve():
    a, b, p = map(int, sys.stdin.buffer.read().split())

    a %= p
    b %= p

    m = math.isqrt(p) + 1

    baby = {}
    cur = 1 % p
    for j in range(m):
        if cur not in baby:
            baby[cur] = j
        cur = cur * a % p

    a_m = pow(a, m, p)
    a_m_inv = pow(a_m, p - 2, p)

    gamma = b
    ans = -1
    for i in range(m + 1):
        if gamma in baby:
            ans = i * m + baby[gamma]
            break
        gamma = gamma * a_m_inv % p

    print(ans)


solve()

Step-by-Step 解説

1指数の平方分割
$x$を$0$から$P-2$まで探すのではなく$x=im+j$に分解する。$m\approx\sqrt P$で$i,j$ともに$O(\sqrt P)$通り。
2baby-step(前計算)
$A^j \bmod P$($j=0,\dots,m-1$)をハッシュ表に格納。同じ値は最小の$j$を優先し、最終的な$x$が最小になるようにする。
3giant-step(探索)
$B\cdot(A^{-m})^i$を$i=0,1,2,\dots$と計算しながらハッシュ表に存在するか確認、見つかれば$x=im+j$を復元する。
4逆元の計算
$P$が素数なのでフェルマーの小定理$A^{P-2}\equiv A^{-1}$を使う。合成数の場合は拡張ユークリッドの互除法や一般化BSGSが必要。
5計算量と正当性
baby-step・giant-stepともに$O(\sqrt P)$、全体で$O(\sqrt P \log P)$。解が存在しない場合は$-1$を返す。

よくあるミス

ミス原因正しい書き方
baby-stepで同じ値のjを上書きする辞書への無条件代入if cur not in baby:で最初の(最小の)jのみ残す
Pが素数でないのにフェルマーの小定理で逆元を求める素数性の確認不足合成数なら拡張ユークリッドや一般化BSGSを使う
mを浮動小数点sqrtの切り捨てで求め精度不足になるfloat平方根の誤差math.isqrt(p)で正確な整数平方根を求める
iの探索範囲がmまでで境界のxを取りこぼすxの最大値に対しiの範囲不足range(m+1)としてP-1未満の全域をカバーする

次のステップ

  • 発展: Pが合成数や$\gcd(A,P)\ne1$の場合の一般化BSGS、Pohlig-Hellman法との組み合わせ
  • 次回予告: オフラインLCA(Tarjanのアルゴリズム)

自己評価

自分の回答

気づき・メモ