問題
形式的冪級数 $F(x) = \sum_{i=0}^{N-1} a_i x^i$($a_0 = 0$、各 $a_i$ は 998244353 での値)が与えられる。$\exp(F(x)) \pmod{x^N}$ を $\bmod 998244353$ で求めよ。
入力形式
N
a_0 a_1 ... a_{N-1}
制約
$2 \leq N \leq 2^{20}$($N$ は 2 のべき乗)
$0 \leq a_i < 998244353$
$a_0 = 0$(exp の定義域条件)
入出力例
入力例 1
4
0 1 0 0
出力例 1
1 1 499122177 166374059
入力例 2
8
0 1 2 3 4 5 6 7
出力例 2
1 1 499122178 500540699 754522046 739786015 547396755 626611024
ヒント (段階的開示)
ヒント1: 方向性
$G = \exp(F)$ を Newton 法(Hensel's Lemma)で求める。精度を倍々に増やしながら収束させる。
ヒント2: アプローチ
Newton 法の反復: $G_{2n} \leftarrow G_n (1 - \log G_n + F) \pmod{x^{2n}}$。
log の計算: $\log G = \int G^{-1} G' \, dx$(微分・積分・逆元 all $O(n \log n)$ NTT)。全体 $O(N \log N)$。ヒント3: 誘導
def poly_exp(F, n, MOD):
# F[0] == 0 が前提
G = [1] # G_1 = 1 = exp(0)
m = 1
while m < n:
m2 = min(m * 2, n)
lnG = poly_log(G, m2, MOD)
correction = poly_sub(poly_add([1], poly_sub(F[:m2], lnG, MOD), MOD), [1], MOD)
G = poly_mul(G, poly_add([1], correction, MOD), MOD)[:m2]
m = m2
return G[:n]
模範解答 (Python)
import sys
from typing import List
input = sys.stdin.readline
MOD = 998244353
g_root = 3 # 原始根
def ntt(a: List[int], invert: bool):
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_root, (MOD - 1) // length, MOD)
if invert:
w = pow(w, MOD - 2, MOD)
for i in range(0, n, length):
wn = 1
for k in range(length // 2):
u = a[i + k]
v = 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 invert:
n_inv = pow(n, MOD - 2, MOD)
for i in range(n):
a[i] = a[i] * n_inv % MOD
def poly_mul(a: List[int], b: List[int]) -> List[int]:
result_len = len(a) + len(b) - 1
n = 1
while n < result_len:
n <<= 1
fa = a + [0] * (n - len(a))
fb = b + [0] * (n - len(b))
ntt(fa, False)
ntt(fb, False)
for i in range(n):
fa[i] = fa[i] * fb[i] % MOD
ntt(fa, True)
return fa[:result_len]
def poly_inv(f: List[int], n: int) -> List[int]:
g = [pow(f[0], MOD - 2, MOD)]
m = 1
while m < n:
m2 = m * 2
fg = poly_mul(f[:m2], g)[:m2]
neg_fg = [(MOD - x) % MOD for x in fg]
neg_fg[0] = (neg_fg[0] + 2) % MOD
g = poly_mul(g, neg_fg)[:m2]
m = m2
return g[:n]
def poly_deriv(f: List[int]) -> List[int]:
return [(i * f[i]) % MOD for i in range(1, len(f))]
def poly_integr(f: List[int]) -> List[int]:
res = [0] * (len(f) + 1)
for i in range(len(f)):
res[i + 1] = f[i] * pow(i + 1, MOD - 2, MOD) % MOD
return res
def poly_log(f: List[int], n: int) -> List[int]:
df = poly_deriv(f[:n])
inv_f = poly_inv(f, n)
return poly_integr(poly_mul(df, inv_f)[:n - 1])[:n]
def poly_exp(f: List[int], n: int) -> List[int]:
assert f[0] == 0
G = [1]
m = 1
while m < n:
m2 = min(m * 2, n)
lnG = poly_log(G, m2)
corr = [0] * m2
corr[0] = (1 - lnG[0] + (f[0] if 0 < len(f) else 0)) % MOD
for i in range(1, m2):
fi = f[i] if i < len(f) else 0
corr[i] = (fi - lnG[i]) % MOD
G_new = poly_mul(G, corr)[:m2]
G = G_new
m = m2
return G[:n]
def solve():
N = int(input())
a = list(map(int, input().split()))
res = poly_exp(a, N)
print(*res)
solve()
Step-by-Step 解説
1形式的冪級数 exp の定義
$\exp(F(x)) = \sum_{n=0}^{\infty} \frac{F(x)^n}{n!}$ を $\pmod{x^N}$ で打ち切る。$F(0) = 0$ が必要条件。
$\exp(F(x)) = \sum_{n=0}^{\infty} \frac{F(x)^n}{n!}$ を $\pmod{x^N}$ で打ち切る。$F(0) = 0$ が必要条件。
2poly_inv(Newton 法で多項式逆元)
$G = F^{-1}$ を求める反復: $G_{2m} = G_m(2 - F \cdot G_m) \pmod{x^{2m}}$。合計 $O(N \log N)$。
$G = F^{-1}$ を求める反復: $G_{2m} = G_m(2 - F \cdot G_m) \pmod{x^{2m}}$。合計 $O(N \log N)$。
3poly_log(微分 + 積分)
$\log F = \int \frac{F'}{F} \, dx$。$F'$ は微分、$F^{-1}$ は Step 2、積分は係数を次数の逆元で割る。
$\log F = \int \frac{F'}{F} \, dx$。$F'$ は微分、$F^{-1}$ は Step 2、積分は係数を次数の逆元で割る。
4poly_exp(Newton 法)
$G_0 = 1$、反復: $G_{2m} \leftarrow G_m \cdot (1 + F - \log G_m) \pmod{x^{2m}}$。各反復で精度倍増、$O(N \log N)$。
$G_0 = 1$、反復: $G_{2m} \leftarrow G_m \cdot (1 + F - \log G_m) \pmod{x^{2m}}$。各反復で精度倍増、$O(N \log N)$。
よくあるミス
| ミス | 原因 | 正しい書き方 |
|---|---|---|
poly_log の長さが $N$ を超える | 積分で長さが1増える | 最後に [:n] でクリップ |
poly_exp の Newton 反復で corr[0] を誤る | log G の定数項は常に 0 | lnG[0] == 0 を確認 |
| NTT のサイズ計算が足りない | mul(a, b) のサイズが len(a)+len(b)-1 | result_len = len(a)+len(b)-1 |
pow(i+1, MOD-2, MOD) を毎回呼ぶ | 積分のたびに逆元計算で遅い | 逆元テーブルを事前計算 |
次のステップ
- 発展問題: FPS の累乗 $F(x)^k \pmod{x^N}$ を $O(N \log N)$ で計算せよ($k$ が巨大な整数の場合含む)