結果
| 問題 | No.3620 Compositional Power with Schröder Coordinate 2 |
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2026-08-10 06:49:46 |
| 言語 | PyPy3 (7.3.17) |
| 結果 |
AC
|
| 実行時間 | 5,057 ms / 10,000 ms |
| + 755µs | |
| コード長 | 41,109 bytes |
| 記録 | |
| コンパイル時間 | 245 ms |
| コンパイル使用メモリ | 95,852 KB |
| 実行使用メモリ | 317,060 KB |
| 最終ジャッジ日時 | 2026-08-10 23:59:30 |
| 合計ジャッジ時間 | 27,664 ms |
|
ジャッジサーバーID (参考情報) |
judge1_1 / judge3_1 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 2 |
| other | AC * 7 |
ソースコード
"""
power projection にも ラグランジュ反転 にも帰着できなかったので変形こねこね
h = g^-1
b_k = \sum_i [x^n] h_i (a^k g)^i
[x^n] g^i が i に対して列挙できれば良い
これは powerprojection
"""
# harurun's library(依存を含む貼り付け用コード)
# 依存: convolution/NTT998.py
"""998244353 固定の高速NTTと係数畳み込み。
`multiply(first, second)` は2つの昇べき順係数列を畳み込み、長さ
`len(first) + len(second) - 1` の係数列を返す。汎用mod判定、原始根探索、
CRTを通らず、固定したradix-4の変換表だけを使う。
"""
MOD = 998244353
PRIMITIVE_ROOT = 3
MAX_LOG = 23
_convolution_ntt998_IMAG = 911660635
_convolution_ntt998_IIMAG = 86583718
_convolution_ntt998_RATE2 = [0, 911660635, 509520358, 369330050, 332049552, 983190778, 123842337, 238493703, 975955924, 603855026, 856644456, 131300601, 842657263, 730768835, 942482514, 806263778, 151565301, 510815449, 503497456, 743006876, 741047443, 56250497, 867605899, 0]
_convolution_ntt998_IRATE2 = [0, 86583718, 372528824, 373294451, 645684063, 112220581, 692852209, 155456985, 797128860, 90816748, 860285882, 927414960, 354738543, 109331171, 293255632, 535113200, 308540755, 121186627, 608385704, 438932459, 359477183, 824071951, 103369235, 0]
_convolution_ntt998_RATE3 = [0, 372528824, 337190230, 454590761, 816400692, 578227951, 180142363, 83780245, 6597683, 70046822, 623238099, 183021267, 402682409, 631680428, 344509872, 689220186, 365017329, 774342554, 729444058, 102986190, 128751033, 395565204, 0]
_convolution_ntt998_IRATE3 = [0, 509520358, 929031873, 170256584, 839780419, 282974284, 395914482, 444904435, 72135471, 638914820, 66769500, 771127074, 985925487, 262319669, 262341272, 625870173, 768022760, 859816005, 914661783, 430819711, 272774365, 530924681, 0]
_convolution_ntt998_INVERSE_SIZE = {1: 1}
def _convolution_ntt998_check_length(size):
if size < 1 or size & size - 1:
raise ValueError('NTT length must be a positive power of two')
if size > 1 << MAX_LOG:
raise ValueError('NTT length exceeds 2^23')
def _convolution_ntt998_butterfly(values):
"""In-place forward radix-4 NTT without coefficient normalization."""
size = len(values)
_convolution_ntt998_check_length(size)
if size == 1:
values[0] %= MOD
return values
height = (size - 1).bit_length()
level = 0
mod = MOD
imag = _convolution_ntt998_IMAG
rate2 = _convolution_ntt998_RATE2
rate3 = _convolution_ntt998_RATE3
while level < height:
if height - level == 1:
width = 1 << height - level - 1
rotation = 1
for block in range(1 << level):
offset = block << height - level
for index in range(width):
left = values[offset + index]
right = values[offset + index + width] * rotation
values[offset + index] = (left + right) % mod
values[offset + index + width] = (left - right) % mod
rotation = rotation * rate2[(~block & -~block).bit_length()] % mod
level += 1
else:
width = 1 << height - level - 2
rotation = 1
for block in range(1 << level):
rotation2 = rotation * rotation % mod
rotation3 = rotation2 * rotation % mod
offset = block << height - level
for index in range(width):
value0 = values[offset + index]
value1 = values[offset + index + width] * rotation
value2 = values[offset + index + 2 * width] * rotation2
value3 = values[offset + index + 3 * width] * rotation3
difference = (value1 - value3) % mod * imag
values[offset + index] = (value0 + value2 + value1 + value3) % mod
values[offset + index + width] = (value0 + value2 - value1 - value3) % mod
values[offset + index + 2 * width] = (value0 - value2 + difference) % mod
values[offset + index + 3 * width] = (value0 - value2 - difference) % mod
rotation = rotation * rate3[(~block & -~block).bit_length()] % mod
level += 2
return values
def _convolution_ntt998_butterfly_inv(values):
"""In-place inverse radix-4 transform without division by the length."""
size = len(values)
_convolution_ntt998_check_length(size)
if size == 1:
values[0] %= MOD
return values
height = (size - 1).bit_length()
level = height
mod = MOD
inverse_imag = _convolution_ntt998_IIMAG
irate2 = _convolution_ntt998_IRATE2
irate3 = _convolution_ntt998_IRATE3
while level:
if level == 1:
width = 1 << height - level
rotation = 1
for block in range(1 << level - 1):
offset = block << height - level + 1
for index in range(width):
left = values[offset + index]
right = values[offset + index + width]
values[offset + index] = (left + right) % mod
values[offset + index + width] = (left - right) * rotation % mod
rotation = rotation * irate2[(~block & -~block).bit_length()] % mod
level -= 1
else:
width = 1 << height - level
rotation = 1
for block in range(1 << level - 2):
rotation2 = rotation * rotation % mod
rotation3 = rotation2 * rotation % mod
offset = block << height - level + 2
for index in range(width):
value0 = values[offset + index]
value1 = values[offset + index + width]
value2 = values[offset + index + 2 * width]
value3 = values[offset + index + 3 * width]
difference = (value2 - value3) * inverse_imag % mod
values[offset + index] = (value0 + value1 + value2 + value3) % mod
values[offset + index + width] = (value0 - value1 + difference) * rotation % mod
values[offset + index + 2 * width] = (value0 + value1 - value2 - value3) * rotation2 % mod
values[offset + index + 3 * width] = (value0 - value1 - difference) * rotation3 % mod
rotation = rotation * irate3[(~block & -~block).bit_length()] % mod
level -= 2
return values
def ntt(values):
"""`values`を破壊的に順変換し、同じlistを返す。O(N log N)。"""
return _convolution_ntt998_butterfly(values)
def _convolution_ntt998__intt(values):
size = len(values)
_convolution_ntt998_butterfly_inv(values)
inverse_size = _convolution_ntt998_INVERSE_SIZE.get(size)
if inverse_size is None:
inverse_size = pow(size, MOD - 2, MOD)
_convolution_ntt998_INVERSE_SIZE[size] = inverse_size
for index in range(size):
values[index] = values[index] * inverse_size % MOD
return values
def intt(values):
"""`values`を破壊的に正規化済み逆変換し、同じlistを返す。O(N log N)。"""
return _convolution_ntt998__intt(values)
def _convolution_ntt998_multiply_naive(first, second):
first_size = len(first)
second_size = len(second)
if first_size == 0 or second_size == 0:
return []
if first_size < second_size:
first, second = (second, first)
first_size, second_size = (second_size, first_size)
result = [0] * (first_size + second_size - 1)
mod = MOD
for index, left in enumerate(first):
left %= mod
if left:
for offset, right in enumerate(second):
result[index + offset] += left * right
if index & 7 == 7:
start = index
stop = min(index + second_size, len(result))
for position in range(start, stop):
result[position] %= mod
return [value % mod for value in result]
def multiply(first, second):
"""2つの係数列の積を長さ`len(first)+len(second)-1`で返す。O(N log N)。"""
first_size = len(first)
second_size = len(second)
if first_size == 0 or second_size == 0:
return []
if min(first_size, second_size) <= 60:
return _convolution_ntt998_multiply_naive(first, second)
output_size = first_size + second_size - 1
size = 1 << (output_size - 1).bit_length()
_convolution_ntt998_check_length(size)
left = [value % MOD for value in first]
left.extend([0] * (size - first_size))
_convolution_ntt998_butterfly(left)
if first is second:
for index in range(size):
left[index] = left[index] * left[index] % MOD
else:
right = [value % MOD for value in second]
right.extend([0] * (size - second_size))
_convolution_ntt998_butterfly(right)
for index in range(size):
left[index] = left[index] * right[index] % MOD
_convolution_ntt998_butterfly_inv(left)
inverse_size = _convolution_ntt998_INVERSE_SIZE.get(size)
if inverse_size is None:
inverse_size = pow(size, MOD - 2, MOD)
_convolution_ntt998_INVERSE_SIZE[size] = inverse_size
for index in range(output_size):
left[index] = left[index] * inverse_size % MOD
del left[output_size:]
return left
def square(series):
"""係数列の二乗を長さ`2*len(series)-1`で返す。O(N log N)。"""
size = len(series)
if size == 0:
return []
if size <= 60:
result = [0] * (2 * size - 1)
for index, left in enumerate(series):
left %= MOD
result[index << 1] += left * left
for offset in range(index + 1, size):
result[index + offset] += 2 * left * series[offset]
return [value % MOD for value in result]
return multiply(series, series)
# 依存: fps998/FPS.py
"""998244353上の形式的冪級数を昇べき順の係数listで計算する。
`a[i]`は$x^i$の係数を表す。inv・log・exp・pow・sqrt、微分・積分、
多項式除算、Taylor shift、一括積を固定modのradix-4 NTTで計算する。
入力listは変更せず、新しい係数listを返す。
"""
from heapq import heapify, heappop, heappush
_fps998_fps_INVERSES = [0, 1]
_fps998_fps_SPARSE_INV_THRESHOLD = 160
_fps998_fps_SPARSE_DIV_THRESHOLD = 200
_fps998_fps_SPARSE_LOG_THRESHOLD = 200
_fps998_fps_SPARSE_EXP_THRESHOLD = 320
_fps998_fps_SPARSE_POWER_THRESHOLD = 32
def _fps998_fps_mod_sqrt(value):
value %= MOD
if value < 2:
return value
if pow(value, MOD - 1 >> 1, MOD) != 1:
return -1
odd = MOD - 1
exponent = 0
while odd & 1 == 0:
odd >>= 1
exponent += 1
nonresidue = 3
root = pow(value, odd + 1 >> 1, MOD)
remainder = pow(value, odd, MOD)
generator = pow(nonresidue, odd, MOD)
level = exponent
while remainder != 1:
position = 1
squared = remainder * remainder % MOD
while position < level and squared != 1:
squared = squared * squared % MOD
position += 1
if position == level:
return -1
adjustment = pow(generator, 1 << level - position - 1, MOD)
root = root * adjustment % MOD
adjustment = adjustment * adjustment % MOD
remainder = remainder * adjustment % MOD
generator = adjustment
level = position
return min(root, MOD - root)
def _fps998_fps_degree(degree, default):
if degree is None:
return default
if degree < 0:
raise ValueError('degree must be nonnegative')
return degree
def _fps998_fps_inverses(size):
if size >= MOD:
raise ValueError('formal integration requires degree < 998244353')
values = _fps998_fps_INVERSES
for index in range(len(values), size + 1):
values.append(-values[MOD % index] * (MOD // index) % MOD)
return values
def _fps998_fps_sparse_terms(series, degree, threshold):
terms = []
for index in range(1, min(len(series), degree)):
value = series[index] % MOD
if value:
terms.append((index, value))
if len(terms) > threshold:
return None
return terms
def _fps998_fps_fps_inv_sparse(series, degree, first_inverse, terms):
result = [0] * degree
result[0] = first_inverse
for index in range(1, degree):
total = 0
for offset, coefficient in terms:
if offset > index:
break
total += coefficient * result[index - offset]
result[index] = -total * first_inverse % MOD
return result
def _fps998_fps_fps_div_sparse(numerator, degree, first_inverse, terms):
result = [0] * degree
for index in range(degree):
total = numerator[index] % MOD if index < len(numerator) else 0
for offset, coefficient in terms:
if offset > index:
break
total -= coefficient * result[index - offset]
result[index] = total * first_inverse % MOD
return result
def _fps998_fps_fps_log_sparse(series, degree, terms):
inverse = _fps998_fps_inverses(degree)
result = [0] * degree
for index in range(1, degree):
total = index * (series[index] % MOD) if index < len(series) else 0
for offset, coefficient in terms:
if offset >= index:
break
total -= (index - offset) * coefficient * result[index - offset]
result[index] = total * inverse[index] % MOD
return result
def _fps998_fps_fps_exp_sparse(degree, terms):
inverse = _fps998_fps_inverses(degree)
result = [0] * degree
result[0] = 1
for index in range(1, degree):
total = 0
for offset, coefficient in terms:
if offset > index:
break
total += offset * coefficient * result[index - offset]
result[index] = total * inverse[index] % MOD
return result
def _fps998_fps_fps_power_unit_sparse(degree, exponent, terms):
inverse = _fps998_fps_inverses(degree)
result = [0] * degree
result[0] = 1
exponent %= MOD
for index in range(1, degree):
total = 0
for offset, coefficient in terms:
if offset > index:
break
factor = (exponent * offset - index + offset) % MOD
total += factor * coefficient * result[index - offset]
result[index] = total * inverse[index] % MOD
return result
def shrink(series):
"""係数をmodで正規化し、末尾の0を除いた新しいlistを返す。O(N)。"""
result = [value % MOD for value in series]
while result and result[-1] == 0:
result.pop()
return result
def fps_add(first, second):
"""2つのFPSを係数ごとに加え、長い方と同じ長さのlistを返す。O(N)。"""
size = max(len(first), len(second))
result = [0] * size
common = min(len(first), len(second))
mod = MOD
for index in range(common):
result[index] = (first[index] + second[index]) % mod
for index in range(common, len(first)):
result[index] = first[index] % mod
for index in range(common, len(second)):
result[index] = second[index] % mod
return result
def fps_sub(first, second):
"""`first-second`の係数列を長い方と同じ長さで返す。O(N)。"""
size = max(len(first), len(second))
result = [0] * size
common = min(len(first), len(second))
mod = MOD
for index in range(common):
result[index] = (first[index] - second[index]) % mod
for index in range(common, len(first)):
result[index] = first[index] % mod
for index in range(common, len(second)):
result[index] = -second[index] % mod
return result
def fps_neg(series):
"""各係数の加法逆元を同じ長さのlistで返す。O(N)。"""
return [-value % MOD for value in series]
def fps_diff(series):
"""形式微分の係数を昇べき順で返す。O(N)。"""
mod = MOD
return [index * series[index] % mod for index in range(1, len(series))]
def fps_integral(series):
"""定数項を0とした形式積分の係数を昇べき順で返す。O(N)。"""
inverse = _fps998_fps_inverses(len(series))
mod = MOD
result = [0] * (len(series) + 1)
for index, value in enumerate(series, 1):
result[index] = value * inverse[index] % mod
return result
def fps_eval(series, value):
"""FPSを多項式とみなし`value`へ代入した値を返す。O(N)。"""
result = 0
value %= MOD
mod = MOD
for coefficient in reversed(series):
result = (result * value + coefficient) % mod
return result
def _fps998_fps_inverse_step(series, result, current, target):
size = current << 1
mod = MOD
left = [value % mod for value in series[:target]]
left.extend([0] * (size - len(left)))
right = result + [0] * (size - current)
_convolution_ntt998_butterfly(left)
_convolution_ntt998_butterfly(right)
for index in range(size):
left[index] = left[index] * right[index] % mod
_convolution_ntt998__intt(left)
for index in range(current):
left[index] = 0
for index in range(current, target):
left[index] = -left[index] % mod
for index in range(target, size):
left[index] = 0
_convolution_ntt998_butterfly(left)
for index in range(size):
left[index] = left[index] * right[index] % mod
_convolution_ntt998__intt(left)
result.extend(left[current:target])
def fps_inv(series, degree=None):
"""$1/f(x)\\bmod x^{degree}$の係数を`degree`個返す。O(N log N)。"""
degree = _fps998_fps_degree(degree, len(series))
if degree == 0:
return []
if not series or series[0] % MOD == 0:
raise ZeroDivisionError('fps inverse requires nonzero constant coefficient')
first_inverse = pow(series[0] % MOD, MOD - 2, MOD)
terms = _fps998_fps_sparse_terms(series, degree, _fps998_fps_SPARSE_INV_THRESHOLD)
if terms is not None:
return _fps998_fps_fps_inv_sparse(series, degree, first_inverse, terms)
result = [first_inverse]
current = 1
while current < degree:
target = min(current << 1, degree)
_convolution_ntt998_check_length(current << 1)
_fps998_fps_inverse_step(series, result, current, target)
current = target
return result
def fps_div(numerator, denominator, degree=None):
"""Return ``numerator / denominator mod x^degree``. O(N log N)."""
degree = _fps998_fps_degree(degree, len(numerator))
if degree == 0:
return []
if not denominator or denominator[0] % MOD == 0:
raise ZeroDivisionError('fps division requires nonzero denominator constant')
first_inverse = pow(denominator[0] % MOD, MOD - 2, MOD)
terms = _fps998_fps_sparse_terms(denominator, degree, _fps998_fps_SPARSE_DIV_THRESHOLD)
if terms is not None:
return _fps998_fps_fps_div_sparse(numerator, degree, first_inverse, terms)
inverse = fps_inv(denominator, degree)
result = multiply(numerator[:degree], inverse)[:degree]
result.extend([0] * (degree - len(result)))
return result
def fps_log(series, degree=None):
"""$\\log f(x)\\bmod x^{degree}$を返す。`f[0]`は1。O(N log N)。"""
degree = _fps998_fps_degree(degree, len(series))
if degree == 0:
return []
if not series or series[0] % MOD != 1:
raise ValueError('fps logarithm requires constant coefficient 1')
terms = _fps998_fps_sparse_terms(series, degree, _fps998_fps_SPARSE_LOG_THRESHOLD)
if terms is not None:
return _fps998_fps_fps_log_sparse(series, degree, terms)
product = multiply(fps_diff(series), fps_inv(series, degree))
result = fps_integral(product[:degree - 1])
result.extend([0] * (degree - len(result)))
return result
def _fps998_fps_fps_exp_ntt(series, degree):
mod = MOD
b = [1, series[1] % mod if len(series) > 1 else 0]
c = [1]
z2 = [1, 1]
inverse = [0, 1]
size = 2
while size < degree:
doubled = size << 1
y = b + [0] * size
_convolution_ntt998_butterfly(y)
z1 = z2
z = [y[index] * z1[index] % mod for index in range(size)]
_convolution_ntt998__intt(z)
for index in range(size >> 1):
z[index] = 0
_convolution_ntt998_butterfly(z)
for index in range(size):
z[index] = -z[index] * z1[index] % mod
_convolution_ntt998__intt(z)
c.extend(z[size >> 1:])
z2 = c + [0] * size
_convolution_ntt998_butterfly(z2)
source_size = min(len(series), size)
x = [series[index] % mod for index in range(source_size)]
x.extend([0] * (size - source_size))
x = fps_diff(x)
x.append(0)
_convolution_ntt998_butterfly(x)
for index in range(size):
x[index] = x[index] * y[index] % mod
_convolution_ntt998__intt(x)
for index in range(1, len(b)):
x[index - 1] = (x[index - 1] - index * b[index]) % mod
x.extend([0] * size)
for index in range(size - 1):
x[size + index], x[index] = (x[index], 0)
_convolution_ntt998_butterfly(x)
for index in range(doubled):
x[index] = x[index] * z2[index] % mod
_convolution_ntt998__intt(x)
x.pop()
for index in range(len(inverse), len(x) + 1):
inverse.append(-inverse[mod % index] * (mod // index) % mod)
x = [0] + [value * inverse[index + 1] % mod for index, value in enumerate(x)]
for index in range(size):
x[index] = 0
for index in range(size, min(len(series), doubled)):
x[index] = (x[index] + series[index]) % mod
_convolution_ntt998_butterfly(x)
for index in range(doubled):
x[index] = x[index] * y[index] % mod
_convolution_ntt998__intt(x)
b.extend(x[size:])
size = doubled
return b[:degree]
def fps_exp(series, degree=None):
"""$\\exp f(x)\\bmod x^{degree}$を返す。`f[0]`は0。O(N log N)。"""
degree = _fps998_fps_degree(degree, len(series))
if degree == 0:
return []
if series and series[0] % MOD:
raise ValueError('fps exponential requires constant coefficient 0')
terms = _fps998_fps_sparse_terms(series, degree, _fps998_fps_SPARSE_EXP_THRESHOLD)
if terms is not None:
return _fps998_fps_fps_exp_sparse(degree, terms)
_convolution_ntt998_check_length(1 << (degree - 1).bit_length())
if degree == 1:
return [1]
return _fps998_fps_fps_exp_ntt(series, degree)
def fps_pow(series, exponent, degree=None):
"""$f(x)^{exponent}\\bmod x^{degree}$を係数`degree`個で返す。O(N log N)。"""
degree = _fps998_fps_degree(degree, len(series))
if degree == 0:
return []
if exponent == 0:
return [1] + [0] * (degree - 1)
leading = 0
while leading < len(series) and series[leading] % MOD == 0:
leading += 1
if leading == len(series):
if exponent < 0:
raise ZeroDivisionError('a zero series cannot have negative exponent')
return [0] * degree
if exponent < 0 and leading:
raise ValueError('negative power requires an invertible series')
shift = leading * exponent
if shift >= degree:
return [0] * degree
coefficient = series[leading] % MOD
inverse_coefficient = pow(coefficient, MOD - 2, MOD)
needed = degree - shift
normalized = [value * inverse_coefficient % MOD for value in series[leading:]]
terms = _fps998_fps_sparse_terms(normalized, needed, _fps998_fps_SPARSE_POWER_THRESHOLD)
if terms is not None:
result = _fps998_fps_fps_power_unit_sparse(needed, exponent, terms)
else:
logarithm = fps_log(normalized, needed)
for index in range(needed):
logarithm[index] = logarithm[index] * exponent % MOD
result = fps_exp(logarithm, needed)
scale = pow(coefficient, exponent, MOD)
return [0] * shift + [value * scale % MOD for value in result]
def fps_sqrt(series, degree=None):
"""$g(x)^2=f(x)\\bmod x^{degree}$となる係数列を返し、なければ`None`。O(N log N)。"""
degree = _fps998_fps_degree(degree, len(series))
if degree == 0:
return []
leading = 0
limit = min(len(series), degree)
while leading < limit and series[leading] % MOD == 0:
leading += 1
if leading == limit:
return [0] * degree
if leading & 1:
return None
shift = leading >> 1
needed = degree - shift
source = [value % MOD for value in series[leading:]]
root = _fps998_fps_mod_sqrt(source[0])
if root == -1:
return None
inverse_constant = pow(source[0], MOD - 2, MOD)
normalized = [value * inverse_constant % MOD for value in source]
terms = _fps998_fps_sparse_terms(normalized, needed, _fps998_fps_SPARSE_POWER_THRESHOLD)
if terms is not None:
result = _fps998_fps_fps_power_unit_sparse(needed, MOD + 1 >> 1, terms)
return [0] * shift + [value * root % MOD for value in result]
inverse_two = MOD + 1 >> 1
result = [root]
current = 1
while current < needed:
target = min(current << 1, needed)
quotient = multiply(source[:target], fps_inv(result, target))[:target]
result.extend([0] * (target - len(result)))
for index in range(target):
value = quotient[index] if index < len(quotient) else 0
result[index] = (result[index] + value) * inverse_two % MOD
current = target
return [0] * shift + result[:needed]
def taylor_shift(series, shift):
"""$f(x+shift)$の係数を`f`と同じ長さのlistで返す。O(N log N)。"""
size = len(series)
if size == 0:
return []
factorial = [1] * size
for index in range(1, size):
factorial[index] = factorial[index - 1] * index % MOD
inverse_factorial = [0] * size
inverse_factorial[-1] = pow(factorial[-1], MOD - 2, MOD)
for index in range(size - 1, 0, -1):
inverse_factorial[index - 1] = inverse_factorial[index] * index % MOD
left = [series[index] * factorial[index] % MOD for index in range(size)]
left.reverse()
right = [0] * size
power = 1
shift %= MOD
for index in range(size):
right[index] = power * inverse_factorial[index] % MOD
power = power * shift % MOD
product = multiply(left, right)
return [product[size - 1 - index] * inverse_factorial[index] % MOD for index in range(size)]
def fps_product(polynomials):
"""複数の多項式をすべて掛けた係数列を返す。O(S log S log K)。"""
heap = []
serial = 0
for polynomial in polynomials:
values = [value % MOD for value in polynomial]
if not values:
return []
heap.append((len(values), serial, values))
serial += 1
if not heap:
return [1]
heapify(heap)
while len(heap) > 1:
_, _, first = heappop(heap)
_, _, second = heappop(heap)
product = multiply(first, second)
heappush(heap, (len(product), serial, product))
serial += 1
return heap[0][2]
# 依存: fps998/PowerProjection.py
"""998244353上で多項式の冪に関する係数をまとめて列挙する。"""
def _fps998_power_projection_power_projection_zero_constant(polynomial, weights, count):
if not polynomial or not weights:
return [0] * count
original_size = len(weights)
size = 1
while size < original_size:
size <<= 1
degree = original_size - 1
height = size
blocks = 1
numerator = [0] * height
denominator = [0] * height
padded_weights = [value % MOD for value in reversed(weights)]
padded_weights.extend([0] * (size - original_size))
numerator[:size] = padded_weights
limit = min(len(polynomial), original_size)
for index in range(1, limit):
denominator[index] = -polynomial[index] % MOD
while degree:
total = 4 * height * blocks
frequency_p = [0] * total
frequency_q = [0] * total
for block in range(blocks):
source = block * height
target = block * height * 2
frequency_p[target:target + degree + 1] = numerator[source:source + degree + 1]
frequency_q[target:target + degree + 1] = denominator[source:source + degree + 1]
frequency_q[blocks * height * 2] = (frequency_q[blocks * height * 2] + 1) % MOD
_convolution_ntt998_butterfly(frequency_p)
_convolution_ntt998_butterfly(frequency_q)
reduced_q = [0] * (total >> 1)
for index in range(0, total, 2):
frequency_q[index], frequency_q[index + 1] = (frequency_q[index + 1], frequency_q[index])
frequency_p[index] = frequency_p[index] * frequency_q[index] % MOD
frequency_p[index + 1] = frequency_p[index + 1] * frequency_q[index + 1] % MOD
reduced_q[index >> 1] = frequency_q[index] * frequency_q[index + 1] % MOD
_convolution_ntt998__intt(frequency_p)
_convolution_ntt998__intt(reduced_q)
reduced_q[0] = (reduced_q[0] - 1) % MOD
child_height = height >> 1
child_degree = degree >> 1
parity = degree & 1
child_size = height * blocks
child_p = [0] * child_size
child_q = [0] * child_size
for block in range(blocks << 1):
source = block * height * 2
target = block * child_height
for index in range(child_degree + 1):
child_p[target + index] = frequency_p[source + (index << 1) + parity]
child_q[target + index] = reduced_q[block * height + index]
numerator = child_p
denominator = child_q
degree >>= 1
height >>= 1
blocks <<= 1
result = numerator[:blocks]
result.reverse()
result = result[:count]
result.extend([0] * (count - len(result)))
return result
def power_projection(polynomial, weights, count):
"""`result[i]=sum_j weights[j][x^j]polynomial(x)^i`を`0<=i<count`で返す。O(N log^2 N)。"""
if count < 0:
raise ValueError('count must be nonnegative')
if count == 0:
return []
if not polynomial or not weights:
return [0] * count
constant = polynomial[0] % MOD
shifted = list(polynomial)
shifted[0] = 0
result = _fps998_power_projection_power_projection_zero_constant(shifted, weights, count)
if constant == 0:
return result
factorial = [1] * count
inverse_factorial = [1] * count
for index in range(1, count):
factorial[index] = factorial[index - 1] * index % MOD
inverse_factorial[-1] = pow(factorial[-1], MOD - 2, MOD)
for index in range(count - 1, 0, -1):
inverse_factorial[index - 1] = inverse_factorial[index] * index % MOD
coefficient = [0] * count
power = 1
for index in range(count):
result[index] = result[index] * inverse_factorial[index] % MOD
coefficient[index] = inverse_factorial[index] * power % MOD
power = power * constant % MOD
result = multiply(result, coefficient)[:count]
result.extend([0] * (count - len(result)))
return [result[index] * factorial[index] % MOD for index in range(count)]
def power_coefficient(polynomial, multiplier=None, count=None):
"""`[x^n]polynomial(x)^i multiplier(x)`を`0<=i<count`で返す。O(N log^2 N)。"""
degree = len(polynomial) - 1
if degree < 0:
return [] if count is None else [0] * count
if multiplier is None:
multiplier = [1]
if count is None:
count = degree + 1
weights = [0] * (degree + 1)
for exponent in range(degree + 1):
multiplier_index = degree - exponent
if multiplier_index < len(multiplier):
weights[exponent] = multiplier[multiplier_index]
return power_projection(polynomial, weights, count)
# 本体
"""998244353上でFPS合成と合成逆関数を計算する。
`fps_compose(outer, inner, degree)`は`outer(inner(x)) mod x^degree`、
`fps_compositional_inv(series, degree)`は`series(g(x))=x mod x^degree`
となる`g`の係数列を返す。
"""
from array import array
def _add_constant(series, value):
if series:
series[0] = (series[0] + value) % MOD
else:
series.append(value % MOD)
def _compose_naive(outer, inner, degree):
result = []
inner = [value % MOD for value in inner[:degree]]
for coefficient in reversed(outer[:degree]):
result = multiply(result, inner)[:degree]
_add_constant(result, coefficient)
result.extend([0] * (degree - len(result)))
return result
def _build_frequency_q(series, n, height, blocks):
total = 4 * height * blocks
frequency = [0] * total
for block in range(blocks):
source = block * height
target = block * height * 2
frequency[target:target + n + 1] = series[source:source + n + 1]
frequency[blocks * height * 2] = (frequency[blocks * height * 2] + 1) % MOD
_convolution_ntt998_butterfly(frequency)
for index in range(0, total, 2):
frequency[index], frequency[index + 1] = (frequency[index + 1], frequency[index])
return frequency
def _descend_q(series, n, height, blocks):
frequency = _build_frequency_q(series, n, height, blocks)
half_total = 2 * height * blocks
reduced = [0] * half_total
for index in range(half_total):
reduced[index] = frequency[index << 1] * frequency[index << 1 | 1] % MOD
_convolution_ntt998__intt(reduced)
reduced[0] = (reduced[0] - 1) % MOD
child_height = height >> 1
child = [0] * (height * blocks)
child_degree = n >> 1
for block in range(blocks << 1):
source = block * height
target = block * child_height
child[target:target + child_degree + 1] = reduced[source:source + child_degree + 1]
return (child, array('I', frequency))
def _reverse_frequency_blocks(values):
start = 1
while start < len(values):
left = start
right = (start << 1) - 1
while left < right:
values[left], values[right] = (values[right], values[left])
left += 1
right -= 1
start <<= 1
def _ascend_p(child, frequency_q, n, height, blocks):
total = len(frequency_q)
frequency_p = [0] * total
child_height = height >> 1
child_degree = n >> 1
parity = n & 1
for block in range(blocks << 1):
source = block * child_height
target = block * height * 2 + parity
for index in range(child_degree + 1):
frequency_p[target + (index << 1)] = child[source + index]
_convolution_ntt998_butterfly(frequency_p)
_reverse_frequency_blocks(frequency_q)
for index in range(total):
frequency_p[index] = frequency_p[index] * frequency_q[index] % MOD
_convolution_ntt998__intt(frequency_p)
result = [0] * (height * blocks)
for block in range(blocks):
source = block * height * 2
target = block * height
result[target:target + n + 1] = frequency_p[source:source + n + 1]
return result
def _compose_ntt(outer, inner, degree):
original_degree = degree - 1
height = 1 << (degree - 1).bit_length()
_convolution_ntt998_check_length(height << 2)
fixed_size = height
outer_values = [value % MOD for value in outer[:degree]]
outer_values.extend([0] * (degree - len(outer_values)))
current = [0] * fixed_size
for index, value in enumerate(inner[:degree]):
current[index] = -value % MOD
frames = []
n = original_degree
block_height = height
blocks = 1
while n:
current, frequency_q = _descend_q(current, n, block_height, blocks)
frames.append((frequency_q, n, block_height, blocks))
n >>= 1
block_height >>= 1
blocks <<= 1
denominator = current[:blocks]
denominator.append(1)
denominator.reverse()
inverse = fps_inv(denominator, len(denominator))
inverse.reverse()
product = multiply(outer_values, inverse)
result = [0] * fixed_size
for index in range(degree):
result[blocks - 1 - index] = product[index + blocks]
while frames:
frequency_q, n, block_height, blocks = frames.pop()
result = _ascend_p(result, frequency_q, n, block_height, blocks)
result = result[:degree]
result.reverse()
return result
def fps_compose(outer, inner, degree=None):
"""`outer(inner(x)) mod x^degree`の係数を`degree`個返す。O(N log^2 N)。"""
if degree is None:
degree = max(len(outer), len(inner))
if degree < 0:
raise ValueError('degree must be nonnegative')
if degree == 0:
return []
if not outer:
return [0] * degree
if len(outer) == 1:
return [outer[0] % MOD] + [0] * (degree - 1)
if len(inner) <= 1:
point = inner[0] % MOD if inner else 0
value = 0
for coefficient in reversed(outer[:degree]):
value = (value * point + coefficient) % MOD
return [value] + [0] * (degree - 1)
if inner[0] % MOD == 0 and inner[1] % MOD == 1 and (len(inner) == 2):
result = [value % MOD for value in outer[:degree]]
result.extend([0] * (degree - len(result)))
return result
if degree <= 64:
return _compose_naive(outer, inner, degree)
return _compose_ntt(outer, inner, degree)
def fps_compositional_inv(series, degree=None):
"""`series(g(x))=x mod x^degree`となる`g`の係数を返す。O(N log^2 N)。"""
if degree is None:
degree = len(series)
if degree < 0:
raise ValueError('degree must be nonnegative')
if degree == 0:
return []
if not series or series[0] % MOD:
raise ValueError('compositional inverse requires series[0] = 0')
if len(series) < 2 or series[1] % MOD == 0:
raise ValueError('a nonzero linear coefficient is required')
inverse_linear = pow(series[1] % MOD, MOD - 2, MOD)
if degree == 1:
return [0]
last = min(len(series), degree) - 1
while last > 1 and series[last] % MOD == 0:
last -= 1
if last == 1:
return [0, inverse_linear] + [0] * (degree - 2)
order = degree - 1
source = [value % MOD for value in series[:degree]]
source.extend([0] * (degree - len(source)))
coefficients = power_coefficient(source, count=degree)
inverses = [0] * degree
inverses[1] = 1
for index in range(2, degree):
inverses[index] = -(MOD // index) * inverses[MOD % index] % MOD
for index in range(1, degree):
coefficients[index] = coefficients[index] * order * inverses[index] % MOD
coefficients.reverse()
scale = pow(coefficients[0], MOD - 2, MOD)
for index in range(degree):
coefficients[index] = coefficients[index] * scale % MOD
exponent = -pow(order, MOD - 2, MOD) % MOD
logarithm = fps_log(coefficients, degree - 1)
for index in range(degree - 1):
logarithm[index] = logarithm[index] * exponent % MOD
result = fps_exp(logarithm, degree - 1)
for index in range(degree - 1):
result[index] = result[index] * inverse_linear % MOD
return [0] + result
mod = MOD
def chirp_z(polynomial, ratio, count=None, start=1):
"""Return f(start*ratio^i) for i=0..count-1 by Bluestein."""
polynomial = [value % mod for value in polynomial]
if count is None:
count = len(polynomial)
if count < 0:
raise ValueError("count must be nonnegative")
if not polynomial or count == 0:
return [0] * count
if start % mod != 1:
power = 1
for i in range(len(polynomial)):
polynomial[i] = polynomial[i] * power % mod
power = power * start % mod
ratio %= mod
if ratio == 0:
result = [polynomial[0]] * count
result[0] = sum(polynomial) % mod
return result
length = len(polynomial)
triangular = [1] * (count + length)
inverse_triangular = [1] * max(count, length)
step = 1
for i in range(1, len(triangular)):
triangular[i] = triangular[i - 1] * step % mod
step = step * ratio % mod
inverse_ratio = pow(ratio, -1, mod)
step = 1
for i in range(1, len(inverse_triangular)):
inverse_triangular[i] = inverse_triangular[i - 1] * step % mod
step = step * inverse_ratio % mod
for i in range(length):
polynomial[i] = polynomial[i] * inverse_triangular[i] % mod
polynomial.reverse()
product = multiply(polynomial, triangular)
return [product[length - 1 + i] * inverse_triangular[i] % mod
for i in range(count)]
n, m = map(int, input().split())
f = list(map(int, input().split()))
g = list(map(int, input().split()))
ig = list(map(int, input().split()))
t = power_projection(g, [0]*(n-1) + [1], n)
t = [t[i]*ig[i]%MOD for i in range(n)]
ans = chirp_z(t, f[1], m)
print(*ans)