Day 059-Q4 — 多項式Taylor Shift(f(x+c) O(NlogN))

2026-06-12 赤色 Master / Phase 8+ ★★★★★★★★★ FPS Taylor Shift / 多項式平行移動 / NTT + 階乗畳み込み

問題

$N-1$ 次多項式 $f(x) = \sum_{i=0}^{N-1} a_i x^i$ と整数 $c$ が与えられる。$f(x+c)$ の係数列 $b_0, b_1, \dots, b_{N-1}$ を $\mathrm{mod}\ 998244353$ で求めよ。

制約

パラメータ範囲
$N$$1 \le N \le 2 \times 10^5$
$c$$0 \le c < 998244353$
$a_i$$0 \le a_i < 998244353$

入出力例

入力例 1

4 2
1 0 1 1

出力例 1

11 18 7 1

$f(x) = x^3 + x^2 + 1$, $f(x+2) = (x+2)^3 + (x+2)^2 + 1$
$= x^3 + 6x^2 + 12x + 8 + x^2 + 4x + 4 + 1 = x^3 + 7x^2 + 16x + 13$
※ 実際の値で確認すること。

概念図: Taylor Shift の畳み込み変換

b_k = Σ_{i≥k} a_i · C(i,k) · c^{i-k} を畳み込みで計算 b_k = (1/k!) · Σ_{i≥k} (a_i · i!) · (c^{i-k} / (i-k)!) これは「A_hat と B の畳み込みの [N-1-k] 番目の係数 × (k!)^{-1}」に変換できる Â[i'] = a[N-1-i'] × (N-1-i')! (逆順 × 階乗) B[j] = c^j / j! (c のべき乗 × 逆階乗) Â: a[N-1]·(N-1)! ... a[1]·1! a[0]·0! B: c⁰/0! c¹/1! ... c^{N-1}/(N-1)! 畳み込み結果 conv = NTT(Â) × NTT(B) → INTT b[k] = conv[N-1-k] × inv_fact[k] mod p 計算量: O(N log N) (N=2×10⁵で高速) key insight: 逆順にして畳み込みにする! 全体フロー: 階乗/逆階乗前計算 O(N) → Â と B 構築 O(N) → NTT O(N log N) → 係数読み出し O(N)

ヒント(段階的開示)

ヒント1: 方向性
Taylor 展開の公式 $f(x+c) = \sum_k \frac{f^{(k)}(c)}{k!} x^k$ をそのまま使うと各 $f^{(k)}(c)$ の計算に $O(N^2)$ かかる。これを NTT による畳み込みで $O(N \log N)$ に落とす。
ヒント2: アプローチ
$$b_k = \sum_{i \ge k} a_i \binom{i}{k} c^{i-k} = \frac{1}{k!} \sum_{i \ge k} (a_i \cdot i!) \cdot \frac{c^{i-k}}{(i-k)!}$$ $\hat{A}[i'] = a_{N-1-i'} \cdot (N-1-i')!$(逆順×階乗), $B[j] = c^j / j!$ とおくと: $$b_k = \text{conv}(\hat{A}, B)[N-1-k] \cdot (k!)^{-1}$$
ヒント3: コード骨格
# 前計算
fact = [1]*(N+1)
for i in range(1,N+1): fact[i]=fact[i-1]*i%MOD
inv_fact = [1]*(N+1)
inv_fact[N] = pow(fact[N],MOD-2,MOD)
for i in range(N-1,-1,-1): inv_fact[i]=inv_fact[i+1]*(i+1)%MOD

A_hat = [a[N-1-i]*fact[N-1-i]%MOD for i in range(N)]
cp=1; B=[]
for j in range(N): B.append(cp*inv_fact[j]%MOD); cp=cp*c%MOD

conv = polymul(A_hat, B)  # NTT畳み込み
b = [conv[N-1-k]*inv_fact[k]%MOD for k in range(N)]

模範解答 (Python)

import sys
input = sys.stdin.readline

MOD = 998244353
g = 3

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 = pow(g, (MOD-1)//length, MOD)
        if inv: w = pow(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:
        inv_n = pow(n, MOD-2, MOD)
        for i in range(n): a[i] = a[i]*inv_n%MOD

def polymul(a, b):
    n = 1
    while n < len(a)+len(b)-1: 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 main():
    N, c = map(int, input().split())
    a = list(map(int, input().split()))

    fact = [1]*(N+1)
    for i in range(1, N+1): fact[i] = fact[i-1]*i%MOD
    inv_fact = [1]*(N+1)
    inv_fact[N] = pow(fact[N], MOD-2, MOD)
    for i in range(N-1, -1, -1): inv_fact[i] = inv_fact[i+1]*(i+1)%MOD

    A_hat = [a[N-1-i]*fact[N-1-i]%MOD for i in range(N)]

    cp = 1
    B = []
    for j in range(N):
        B.append(cp*inv_fact[j]%MOD)
        cp = cp*c%MOD

    conv = polymul(A_hat, B)

    b = [conv[N-1-k]*inv_fact[k]%MOD for k in range(N)]
    print(*b)

main()

Step-by-Step 解説

Step 1: Taylor Shift の公式

$$b_k = [x^k]f(x+c) = \sum_{i=k}^{N-1} a_i \binom{i}{k} c^{i-k}$$

Step 2: 畳み込みへの変換

$b_k = \frac{1}{k!}\sum_{i=k}^{N-1}(a_i \cdot i!) \cdot \frac{c^{i-k}}{(i-k)!}$。 $i' = N-1-i$ と置換すると、これは $\hat{A}[i'] = a_{N-1-i'} \cdot (N-1-i')!$ と $B[j] = c^j/j!$ の畳み込みの $[N-1-k]$ 番目の係数。

Step 3: NTT で計算

$\hat{A}$ と $B$ の畳み込みを NTT で $O(N \log N)$。

Step 4: 係数の読み出し

$b_k = \text{conv}[N-1-k] \cdot (k!)^{-1} \bmod p$。

よくあるミス

ミス原因正しい書き方
A_hat[i] = a[i]*fact[i]逆順にしていないA_hat[i] = a[N-1-i]*fact[N-1-i]
conv[N-1+k] を参照オフセット計算ミスconv[N-1-k]
$c=0$ で B が全て0になると誤解$B[0] = c^0/0! = 1$問題なし (正常動作)

次のステップ

発展問題: $f(x+1), f(x+2), \dots, f(x+K)$ を一括で求める多点 Taylor Shift を実装せよ。

自己評価

解いた後に記入してください。