Day 014-Q4 — 多項式の合成と逆元(FPS)

2026-04-27 赤色 Master / Phase 8+ ★★★★★★★★★ NTT / Newton 法

問題

次数 N の多項式 $F(x)$ (係数 mod $p = 998244353$) について、(1) 微分, (2) 積分, (3) 逆元, (4) log, (5) exp を計算。

制約

$1 \le N \le 2 \times 10^5$
$0 \le a_i < p$

入出力例

入力例 1

4
1 2 3 4 5

出力例 1

# 微分: 2 6 12 20
# 積分: 0 1 1 1 1 1
# 逆元: 1 998244351 1 998244349

ヒント (段階的開示)

ヒント1: 方向性
NTT で多項式乗算 $O(N \log N)$。逆元・log・exp は Newton 法で $O(N \log N)$。
ヒント2: アプローチ
逆元 Newton: $G_{k+1} = G_k(2 - F G_k)$。exp Newton: $H_{k+1} = H_k(1 - \log H_k + F)$。
ヒント3: 誘導
log = ∫(F'/F) = 微分 + 逆元 + 積分。MOD = 998244353 は NTT-friendly。

模範解答 (Python)

import sys
input = sys.stdin.readline

MOD = 998244353
g = 3

def power(a, b, mod):
    res = 1; a %= mod
    while b > 0:
        if b & 1:
            res = res * a % mod
        a = a * a % mod
        b >>= 1
    return res

def ntt(a, inv=False):
    n = len(a)
    j = 0
    for i in range(1, n):
        bit = n >> 1
        while j & bit:
            j ^= bit; bit >>= 1
        j ^= bit
        if i < j:
            a[i], a[j] = a[j], a[i]
    length = 2
    while length <= n:
        w = power(g, (MOD - 1) // length, MOD)
        if inv:
            w = power(w, MOD - 2, MOD)
        for i in range(0, n, length):
            wn = 1
            for k in range(length // 2):
                u, v = a[i+k], a[i+k+length//2] * wn % MOD
                a[i+k] = (u + v) % MOD
                a[i+k+length//2] = (u - v) % MOD
                wn = wn * w % MOD
        length <<= 1
    if inv:
        n_inv = power(n, MOD - 2, MOD)
        for i in range(n):
            a[i] = a[i] * n_inv % MOD

def poly_mul(a, b):
    n = 1
    while n < len(a) + len(b):
        n <<= 1
    fa = a[:] + [0] * (n - len(a))
    fb = b[:] + [0] * (n - len(b))
    ntt(fa); ntt(fb)
    for i in range(n):
        fa[i] = fa[i] * fb[i] % MOD
    ntt(fa, inv=True)
    return fa

def poly_inv(f, n):
    g_arr = [power(f[0], MOD - 2, MOD)]
    length = 1
    while length < n:
        length <<= 1
        fg = poly_mul(f[:length], g_arr)[:length]
        neg_fg = [(2 - x) % MOD if i == 0 else (-x) % MOD for i, x in enumerate(fg)]
        g_arr = poly_mul(g_arr, neg_fg)[:length]
    return g_arr[:n]

def poly_deriv(f):
    return [i * f[i] % MOD for i in range(1, len(f))]

def poly_integr(f):
    n = len(f)
    inv_i = [0, 1]
    for i in range(2, n + 1):
        inv_i.append(-(MOD // i) * inv_i[MOD % i] % MOD)
    return [0] + [f[i] * inv_i[i+1] % MOD for i in range(n)]

def solve():
    N = int(input())
    a = list(map(int, input().split()))
    deriv = poly_deriv(a)
    print("Derivative:", deriv)
    integ = poly_integr(a)
    print("Integral:", integ[:N+1])
    if a[0] != 0:
        inv_f = poly_inv(a, N)
        print("Inverse:", inv_f[:N])

solve()

Step-by-Step 解説

1NTT
MOD = $998244353 = 119 \times 2^{23} + 1$ で原始根 g=3。FFT の有限体版。
2逆元の Newton 法
$G_{k+1} = G_k(2 - F G_k)$ で精度が倍に、$\log N$ 回で完成。
3log/exp
log = ∫(F'/F)、exp は Newton 法。いずれも $O(N \log N)$。

よくあるミス

ミス原因正しい書き方
poly_mul 後の長さ超過結果が 2N-1[:n] でトリム
逆数計算の負数Python の % 注意常に % MOD
exp の初期値H_0 = 0 にしてしまうH_0 = [1]

次のステップ

  • 多項式合成 $F(G(x))$ を $O(N^{1.5} \log N)$ で

自己評価

自分の回答

気づき・メモ