問題
形式的冪級数 $f(x) = \sum_{k=0}^{N-1} a_k x^k$ と $g(x) = \sum_{k=0}^{N-1} b_k x^k$($b_0 = 0$)が与えられる。 $h(x) = f(g(x)) \bmod x^N$ を求めよ。係数は $p = 998244353$ で mod を取ること。
制約
$1 \le N \le 1000$
$b_0 = 0$(合成可能条件)
$0 \le a_i, b_i < p$
時間制限: 2sec / メモリ: 256MB
入出力例
入力例 1
4
1 1 1 1
0 1 1 0
出力例 1
1 1 3 4
$f(x) = 1+x+x^2+x^3$, $g(x) = x+x^2$, $f(g(x)) \bmod x^4$ の係数列
概念図: Baby-step Giant-step 多項式合成
ヒント(段階的開示)
ヒント1: 方向性
$h(x) = f(g(x)) = \sum_{k=0}^{N-1} a_k g(x)^k$。$b_0 = 0$ なので $g^k \bmod x^N$ は $k$ 次から始まる。
愚直: $g^k$ を順次求めて $a_k g^k$ を加算 → $O(N^3)$($N \le 1000$ なら通る)。
ヒント2: アプローチ
Baby-step Giant-step: $K = \lfloor\sqrt{N}\rfloor$ として
- Baby: $g^0, g^1, ..., g^{K-1}$ を事前計算 ($K$ 個)
- Giant: $G = g^K$ を計算し、$G^0, G^1, ...$ を準備 ($K$ 個)
- $f = \sum_{j} (\sum_i a_{jK+i} g^i) G^j$ と分解して $O(K^2 \cdot N \log N)$ で計算
ヒント3: 実装骨格
MOD = 998244353
def poly_mul_mod(p, q, N):
res = [0] * N
for i in range(N):
if p[i] == 0: continue
for j in range(N - i):
res[i+j] = (res[i+j] + p[i] * q[j]) % MOD
return res
# 愚直 O(N^3)
gk = [0] * N; gk[0] = 1 # g^0 = 1
result = [0] * N
for k in range(N):
if a[k]: result = [r + a[k]*g for r, g in zip(result, gk)]...
gk = poly_mul_mod(gk, b, N)
模範解答 (Python)
import sys
input = sys.stdin.readline
MOD = 998244353
def solve():
N = int(input())
a = list(map(int, input().split()))
b = list(map(int, input().split()))
def poly_mul_mod(p, q):
res = [0] * N
for i in range(N):
if p[i] == 0: continue
for j in range(N - i):
res[i+j] = (res[i+j] + p[i] * q[j]) % MOD
return res
# 愚直 O(N^3): N<=1000 なら PyPy で通る
gk = [0] * N
gk[0] = 1 # g^0 = 1
result = [0] * N
for k in range(N):
if a[k] != 0:
for i in range(N):
result[i] = (result[i] + a[k] * gk[i]) % MOD
if k < N - 1:
gk = poly_mul_mod(gk, b)
print(*result)
solve()
Step-by-Step 解説
1合成の定義と有限性
$h = f(g) = \sum_k a_k g^k$。$b_0 = 0$ なので $g^k$ の最低次数は $k$。$k \ge N$ では mod $x^N$ が 0 になるため、$k = 0$ から $N-1$ までの有限和で計算できる。
$h = f(g) = \sum_k a_k g^k$。$b_0 = 0$ なので $g^k$ の最低次数は $k$。$k \ge N$ では mod $x^N$ が 0 になるため、$k = 0$ から $N-1$ までの有限和で計算できる。
2愚直法の実装
gk = g^k を前ステップの g^{k-1} に g を掛けて順次生成。各ステップで a[k] * gk を result に加算。$O(N)$ ステップ × $O(N^2)$ 乗算 = $O(N^3)$。
3Baby-step Giant-step の概要
$K = \lceil\sqrt{N}\rceil$ として Giant step $G = g^K$ を事前計算。$f = \sum_j P_j(g) \cdot G^j$ と分解し、$P_j$ の計算に Baby step の結果を使う。各乗算を NTT $O(N \log N)$ にすれば全体 $O(N^{1.5} \log N)$。
$K = \lceil\sqrt{N}\rceil$ として Giant step $G = g^K$ を事前計算。$f = \sum_j P_j(g) \cdot G^j$ と分解し、$P_j$ の計算に Baby step の結果を使う。各乗算を NTT $O(N \log N)$ にすれば全体 $O(N^{1.5} \log N)$。
4NTT との組み合わせ
$N > 1000$ では多項式乗算を NTT に置き換える。Python では外部ライブラリか PyPy の高速実装が必要。Library Checker の問題では $N \le 2 \times 10^4$ が標準的な制約。
$N > 1000$ では多項式乗算を NTT に置き換える。Python では外部ライブラリか PyPy の高速実装が必要。Library Checker の問題では $N \le 2 \times 10^4$ が標準的な制約。
計算量
愚直: $O(N^3)$($N \le 1000$)
BSGS + 愚直乗算: $O(N^{2.5})$
BSGS + NTT: $O(N^{1.5} \log N)$
空間: $O(N)$(多項式 K 個分)
BSGS + 愚直乗算: $O(N^{2.5})$
BSGS + NTT: $O(N^{1.5} \log N)$
空間: $O(N)$(多項式 K 個分)
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
b[0] != 0 で誤った合成 | 非合成可能な入力 | 事前に assert b[0] == 0 |
gk の更新を忘れる | ループ外で更新漏れ | ループ末尾で gk = poly_mul_mod(gk, b) |
| 係数の mod 取り忘れ | 中間値がオーバーフロー | 各演算後に % MOD |
| N=1000 で O(N^3) TLE | Python の定数が大きい | PyPy 使用 or BSGS に切り替え |
次のステップ
- 発展問題: $N \le 2 \times 10^4$ の合成(Brent-Kung $O(N^{1.5} \log N)$)
- 類題: Library Checker "Composition of Formal Power Series"
- 応用: 関数の反復合成 $f^k(x)$、EGF の変数変換