結果
| 問題 | No.1549 [Cherry 2nd Tune] BANning Tuple |
| コンテスト | |
| ユーザー |
|
| 提出日時 | 2026-05-05 17:33:16 |
| 言語 | PyPy3 (7.3.17) |
| 結果 |
AC
|
| 実行時間 | 2,434 ms / 4,000 ms |
| コード長 | 6,553 bytes |
| 記録 | |
| コンパイル時間 | 137 ms |
| コンパイル使用メモリ | 85,248 KB |
| 実行使用メモリ | 175,096 KB |
| 最終ジャッジ日時 | 2026-05-05 17:33:54 |
| 合計ジャッジ時間 | 35,768 ms |
|
ジャッジサーバーID (参考情報) |
judge2_1 / judge1_1 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| other | AC * 20 |
ソースコード
## https://yukicoder.me/problems/no/1549
# 数論変換パートは
# https://qiita.com/AngrySadEight/items/0dfde26060daaf6a2fda
# と
# https://qiita.com/izu_nori/items/1c5cdef0500ffa0276f5
# を参考にしました
MOD = 998244353
class SegmentTree:
"""
非再帰版セグメント木。
更新は「加法」、取得は「最大値」のもの限定。
"""
def __init__(self, init_array, max_t):
n = 1
while n < len(init_array):
n *= 2
self.max_t = max_t
self.size = n
self.array = [None for _ in range(2 * self.size)]
self.ntt = NTT()
def _op(self, left, right):
if left is None and right is None:
return None
elif left is None:
return right.copy()
elif right is None:
return left.copy()
else:
poly = self.ntt.convolution(left, right)
return poly[0:(self.max_t + 1)]
def set(self, x, a, b):
index = self.size + x
if self.array[index] is None:
self.array[index] = [1] * (1 + self.max_t)
for i in range(a, b + 1):
if 0 <= i <= self.max_t:
self.array[index][i] = 0
while index > 1:
index //= 2
self.array[index] = self._op(self.array[2 * index], self.array[2 * index + 1])
def convolution(self, l, r):
L = self.size + l; R = self.size + r
# 2. 区間[l, r)の最大値を求める
s = None
while L < R:
if R & 1:
R -= 1
s = self._op(s, self.array[R])
if L & 1:
s = self._op(s, self.array[L])
L += 1
L >>= 1; R >>= 1
return s
class NTT:
def __init__(self):
self._root = self._make_root()
self._invroot = self._make_invroot(self._root)
def _reverse_bits(self, n):
n = (n >> 16) | (n << 16)
n = ((n & 0xff00ff00) >> 8) | ((n & 0x00ff00ff) << 8)
n = ((n & 0xf0f0f0f0) >> 4) | ((n & 0x0f0f0f0f) << 4)
n = ((n & 0xcccccccc) >> 2) | ((n & 0x33333333) << 2)
n = ((n & 0xaaaaaaaa) >> 1) | ((n & 0x55555555) << 1)
return n
def _make_root(self):
# 3はMODの原始根, 119乗するとconvolusion, NTT における「基底」の条件を満たす
r = pow(3, 119, MOD)
return [pow(r, 2 ** i, MOD) for i in range(23, -1, -1)]
def _make_invroot(self, root):
invroot = []
for i in range(len(root)):
invroot.append(pow(root[i], MOD - 2, MOD))
return invroot
def _ntt(self, poly, root, rev, max_l):
n = len(poly)
k = (n - 1).bit_length()
step = (max_l) >> k
for i, j in enumerate(rev[::step]):
if i < j:
poly[i], poly[j] = poly[j], poly[i]
r = 1
for w in root[1:(k + 1)]:
for l in range(0, n, r * 2):
wi = 1
for i in range(r):
a = (poly[l + i + r] * wi) % MOD
a += poly[l + i]
a %= MOD
b = (-poly[l + i + r] * wi) % MOD
b += poly[l + i]
b %= MOD
poly[l + i] = a
poly[l + i + r] = b
wi *= w
wi %= MOD
r <<= 1
def convolution(self, poly_l, poly_r):
# 多項式を畳み込んだ時の次数よりも大きい2の冪の長さを求める
# (NTTの特性上2の冪乗に乗せるため)
len_ans = len(poly_l) + len(poly_r) - 1
if (min(len(poly_l), len(poly_r)) <= 40):
return self._combolution_light(poly_l, poly_r)
# 2の冪の長さを求める
n = 1
max_depth = 0
while n <= len_ans:
n *= 2
max_depth += 1
rev = [self._reverse_bits(i) >> (32- max_depth) for i in range(n)]
new_poly_l = [0] * n
for i in range(len(poly_l)):
new_poly_l[i] = poly_l[i]
new_poly_r = [0] * n
for i in range(len(poly_r)):
new_poly_r[i] = poly_r[i]
# 数論変換
self._ntt(new_poly_l, self._root, rev, n)
self._ntt(new_poly_r, self._root, rev, n)
# 畳み込みは各iを代入した値の積で求められる
d_ans = [0] * n
for i in range(n):
d_ans[i] = (new_poly_l[i] * new_poly_r[i]) % MOD
# 逆数論変換
self._ntt(d_ans, self._invroot, rev, n)
# 最後の定数分割る処理
inv_n = pow(n, MOD - 2, MOD)
poly_ans = [0] * len_ans
for i in range(len_ans):
poly_ans[i] = (d_ans[i] * inv_n) % MOD
return poly_ans
def _combolution_light(self, poly_l, poly_r):
poly_ans = [0] * (len(poly_l) + len(poly_r) - 1)
for i in range(len(poly_l)):
for j in range(len(poly_r)):
poly_ans[i + j] += (poly_l[i] * poly_r[j]) % MOD
poly_ans[i + j] %= MOD
return poly_ans
inv_ = [0] * 3100
inv_[0] = 1
for i in range(1, 3100):
inv_[i] = pow(i, MOD - 2, MOD)
def main():
N, Q = map(int, input().split())
kabst = []
max_t = 0
k_set = set()
for _ in range(Q):
K, A, B, S, T = map(int, input().split())
max_t = max(max_t, T)
kabst.append((K, A, B, S, T))
k_set.add(K)
k_list = list(k_set)
k_list.sort()
k_map = {}
for i, k in enumerate(k_list):
k_map[k] = i
ntt = NTT()
seg_tree = SegmentTree([None for _ in range(len(k_map))], max_t)
poly_map = {}
for K, A, B, S, T in kabst:
if K not in poly_map:
poly_map[K] = 1
seg_tree.set(k_map[K], A, B)
poly = seg_tree.convolution(0, seg_tree.size)
l = len(poly_map)
if N - l > 0:
new_poly = [0] * (T + 1)
new_poly[0] = 1
ans = 1
for v in range(1, T + 1):
ans *= (N - 1 - l + v) % MOD
ans %= MOD
ans *= inv_[v]
ans %= MOD
new_poly[v] = ans
poly = ntt.convolution(new_poly, poly)
poly = poly[0:(T + 1)]
answer = 0
for v in range(S, T + 1):
answer += poly[v]
answer %= MOD
print(answer)
if __name__ == "__main__":
main()