Day 086-Q5 — Garner のアルゴリズム / 一般 CRT(非互いに素な合同式の統合)

2026-07-09 赤色 Master / Phase 8+ ★★★★★★★★★ 中国剰余定理・gcd・モジュラ逆元

問題

$N$ 個の合同式 $x \equiv r_i \pmod{m_i}$ が与えられる。法 $m_i$ は互いに素とは限らない。すべてを満たす最小の非負整数 $x$ を求めよ。解が存在しなければ -1 を出力。

制約

パラメータ範囲備考
$N$$1 \le N \le 10^5$合同式の個数
$m_i$$1 \le m_i \le 10^9$法(互いに素とは限らない)
$r_i$$0 \le r_i < m_i$余り

入出力例

入力例1

3
2 3
3 5
2 7

出力例1

23

$23 \equiv 2 \pmod 3,\ 23 \equiv 3 \pmod 5,\ 23 \equiv 2 \pmod 7$。最小の非負解。

概念図: 2 式の逐次統合

(r,m) に 1 式ずつ畳み込む — g=gcd で両立性を判定 x≡0 (mod 1)初期 x≡2 (mod 3)統合後 x≡8 (mod 15)統合後 x≡23 (mod 105) +(2,3) +(3,5) +(2,7) 統合式(x = r₁ + m₁·t を第2式に代入) 両立条件: (r₂ − r₁) mod g == 0 (g = gcd(m₁, m₂)、不成立なら解なし) t ≡ (r₂−r₁)/g · inv(m₁/g) (mod m₂/g), 新 r = (r₁ + m₁·t) mod lcm

ヒント

ヒント1(方向性)

2 式 $(r_1,m_1),(r_2,m_2)$ を統合。$g=\gcd(m_1,m_2)$ とし $(r_2-r_1)$ が $g$ で割り切れなければ解なし。割り切れれば法 $\mathrm{lcm}$ の 1 式にまとまる。

ヒント2(アプローチ)

$x=r_1+m_1t$ と置き $\frac{m_1}{g}t \equiv \frac{r_2-r_1}{g} \pmod{m_2/g}$。$\frac{m_1}{g}$ は $\frac{m_2}{g}$ と互いに素なので逆元が取れる。

ヒント3(ほぼ答え)
def merge(r1,m1,r2,m2):
    g = gcd(m1,m2)
    if (r2-r1)%g: return None
    lcm = m1//g*m2
    t = ((r2-r1)//g) * pow(m1//g, -1, m2//g) % (m2//g)
    return (r1 + m1*t) % lcm, lcm

模範解答

import sys
from math import gcd

def solve():
    data = sys.stdin.buffer.read().split()
    N = int(data[0])
    r, m = 0, 1  # 恒真な合同式から開始
    ok = True
    for i in range(N):
        ri = int(data[1 + 2 * i]); mi = int(data[2 + 2 * i])
        g = gcd(m, mi)
        if (ri - r) % g != 0:
            ok = False
            break
        lcm = m // g * mi
        m2g = mi // g
        t = ((ri - r) // g) * pow(m // g, -1, m2g) % m2g
        r = (r + m * t) % lcm
        m = lcm
    print(r if ok else -1)

solve()

計算量: 1 式あたり $\gcd$ と逆元で $O(\log m)$、全体 $O(N \log \max m_i)$。pow(a, -1, mod) は Python 3.8+ のモジュラ逆元。

Step-by-Step 解説

Step 1: 2 式の統合条件

解を持つ必要十分条件は $r_1 \equiv r_2 \pmod{\gcd(m_1,m_2)}$。

Step 2: パラメータ表現

$x=r_1+m_1t$ を代入し両辺を $g$ で割ると $\frac{m_1}{g}t \equiv \frac{r_2-r_1}{g} \pmod{m_2/g}$。

Step 3: 逆元で $t$ を決定

$\gcd(m_1/g, m_2/g)=1$ より pow(m1//g, -1, m2//g) が存在。$x=(r_1+m_1t)\bmod \mathrm{lcm}$。

Step 4: 逐次畳み込み

初期 $(r,m)=(0,1)$ から 1 式ずつ統合。途中で条件不成立なら -1

よくあるミス

ミス原因正しい書き方
互いに素前提で単純積$\gcd\ne1$ で誤答lcm=m//g*mi、逆元の法は $m_2/g$
負の差での判定言語により % の符号Python では (ri-r)%g で正しく判定可
逆元の法を $m_2$ にする$m_1/g$ は $m_2$ と非互素の場合あり法は $m_2/g$

次のステップ

  • 発展問題: 巨大法($\prod m_i$ が 64bit 超)を任意 mod で復元する Garner の一般形
  • 発展問題: 複数素数 NTT の結果統合(Garner による多倍長回復)

自己評価

理解度: / /

自分の回答:

気づき・メモ: