## 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()