問題
線形合同式(Type 1)、連立合同式 CRT(Type 2)、離散対数 BSGS(Type 3)の 3 種クエリに答えよ。
制約
$1 \le Q \le 10^5$
$1 \le m, m_1, m_2, n \le 10^9$
入出力例
入力例 1
4
1 6 10 14
1 3 6 9
2 2 3 3 5
3 2 32 1000000007出力例 1
4
2
8 15
-1ヒント (段階的開示)
ヒント1: 方向性
Type 1: ext_gcd で逆元 / Type 2: 一般 CRT / Type 3: BSGS(一般版)。
ヒント2: アプローチ
$\gcd(a, m) \mid b$ が解存在条件。$x = s \cdot (b/g) \bmod (m/g)$。CRT は $r_1 + m_1 k$ 形で Type 1 に帰着。
ヒント3: 一般 BSGS
$\gcd(a,n) > 1$ なら前処理で $\gcd$ を剥がしてから通常 BSGS。
模範解答 (Python)
import sys
from sys import stdin
from math import isqrt, gcd
def ext_gcd(a, b):
if b == 0:
return a, 1, 0
g, x, y = ext_gcd(b, a % b)
return g, y, x - (a // b) * y
def linear_congruence(a, b, m):
a %= m; b %= m
if a == 0:
return 0 if b == 0 else -1
g, s, _ = ext_gcd(a, m)
if b % g != 0:
return -1
mod = m // g
x0 = s * (b // g) % mod
return x0 % mod
def crt(r1, m1, r2, m2):
g = gcd(m1, m2)
if (r2 - r1) % g != 0:
return -1, -1
lcm = m1 // g * m2
_, s, _ = ext_gcd(m1, m2)
k = s * ((r2 - r1) // g) % (m2 // g)
x = (r1 + m1 * k) % lcm
if x < 0:
x += lcm
return x, lcm
def bsgs(a, b, n):
t = 0
cur_n = n
while True:
g = gcd(a, cur_n)
if g == 1:
break
if b % g != 0:
return -1
b //= g
cur_n //= g
t += 1
if t > 100:
return -1
m = isqrt(cur_n) + 1
b_table = {}
val = b % cur_n
for j in range(m):
if val not in b_table:
b_table[val] = j
val = val * a % cur_n
am = pow(a, m, cur_n)
val = am
for i in range(1, m + 1):
if val in b_table:
x = m * i - b_table[val]
if x >= 0:
return x + t
val = val * am % cur_n
return -1
def solve():
sys.setrecursionlimit(200000)
data = stdin.read().split()
idx = 0
Q = int(data[idx]); idx += 1
out = []
for _ in range(Q):
qtype = int(data[idx]); idx += 1
if qtype == 1:
a, b, m = int(data[idx]), int(data[idx+1]), int(data[idx+2]); idx += 3
out.append(str(linear_congruence(a, b, m)))
elif qtype == 2:
r1, m1, r2, m2 = int(data[idx]), int(data[idx+1]), int(data[idx+2]), int(data[idx+3]); idx += 4
x, lcm = crt(r1, m1, r2, m2)
out.append('-1' if x == -1 else f'{x} {lcm}')
elif qtype == 3:
a, b, n = int(data[idx]), int(data[idx+1]), int(data[idx+2]); idx += 3
out.append(str(bsgs(a, b, n)))
print('\n'.join(out))
solve()
Step-by-Step 解説
1拡張ユークリッド
$\gcd(a,b) = as + bt$ を再帰で $O(\log \min(a,b))$。
$\gcd(a,b) = as + bt$ を再帰で $O(\log \min(a,b))$。
2線形合同式
解存在 ⟺ $g \mid b$。解 $\equiv s \cdot b/g \pmod{m/g}$。
解存在 ⟺ $g \mid b$。解 $\equiv s \cdot b/g \pmod{m/g}$。
3一般 CRT
$g = \gcd(m_1, m_2)$、解存在 ⟺ $g \mid r_2 - r_1$。
$g = \gcd(m_1, m_2)$、解存在 ⟺ $g \mid r_2 - r_1$。
4BSGS
$x = mi - j$ 分解、ハッシュ + giant step で $O(\sqrt{n})$。
$x = mi - j$ 分解、ハッシュ + giant step で $O(\sqrt{n})$。
5一般 BSGS
前処理で $\gcd(a,n)$ を剥がし、$t$ を解に加算。
前処理で $\gcd(a,n)$ を剥がし、$t$ を解に加算。
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
| $g \nmid b$ チェック忘れ | 解存在条件漏れ | 必ず確認 |
| CRT の lcm オーバーフロー | $m_1 \cdot m_2$ が大きい | m1 // g * m2 |
| BSGS で x < 0 | $mi - j$ 負になる | x >= 0 をチェック |
| 最小非負解でない | 負の値が返る | % mod 後に補正 |
次のステップ
- $K$ 個の連立 CRT + Garner