Problem:


Score : 500 points

Problem Statement

You have a string S of length N.
Initially, all characters in S are 1s.

You will perform queries Q times.
In the i-th query, you are given two integers L_i, R_i and a character D_i (which is a digit).
Then, you must replace all characters from the L_i-th to the R_i-th (inclusive) with D_i.

After each query, read the string S as a decimal integer, and print its value modulo 998,244,353.

Constraints

1 \leq N, Q \leq 200,000

1 \leq L_i \leq R_i \leq N

1 \leq D_i \leq 9

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N Q
L_1 R_1 D_1
:
L_Q R_Q D_Q

Output

Print Q lines.
In the i-th line print the value of S after the i-th query, modulo 998,244,353.

Sample Input 1

8 5
3 6 2
1 4 7
3 8 3
2 2 2
4 5 1

Sample Output 1

11222211
77772211
77333333
72333333
72311333

Sample Input 2

200000 1
123 456 7

Sample Output 2

641437905

Don't forget to take the modulo.



Sample input:
8 5
3 6 2
1 4 7
3 8 3
2 2 2
4 5 1


Sample output:
11222211
77772211
77333333
72333333
72311333

Submission status: Accepted

Solution code:
import sys
input = lambda : sys.stdin.readline().rstrip()
sys.setrecursionlimit(max(1000, 10**9))
write = lambda x: sys.stdout.write(x+"\n")



### 遅延評価セグメント木
class LazySegmentTree:
    def __init__(self, n, a=None):
        """初期化
        num : n以上の最小の2のべき乗
        """
        num = 1
        while num<=n:
            num *= 2
        self.num = num
        self.seg = [ninf] * (2*self.num-1)
        self.lazy = [f0] * (2*self.num-1)
        self.ls = [0]*(2*self.num-1)
        self.rs = [0]*(2*self.num-1)
        self.ls[0] = 0
        self.rs[0] = self.num
        for i in range(self.num-1):
            self.ls[2*i+1] = self.ls[i]
            self.rs[2*i+1] = (self.ls[i] + self.rs[i])//2
            self.ls[2*i+2] = (self.ls[i] + self.rs[i])//2
            self.rs[2*i+2] = self.rs[i]
        if a is not None:
            # O(n)で初期化
            assert len(a)==n
            for i in range(n):
                self.seg[num-1+i] = a[i]
            for k in range(num-2, -1, -1):
                self.seg[k] = op(self.seg[2*k+1], self.seg[2*k+2])
    def eval(self, k):
        if self.lazy[k]==f0:
            return 
        if k<self.num-1:
            self.lazy[k*2+1] = composition(self.lazy[k], self.lazy[k*2+1])
            self.lazy[k*2+2] = composition(self.lazy[k], self.lazy[k*2+2])
        self.seg[k] = mapping(self.lazy[k], self.seg[k])
        self.lazy[k] = f0
    def eval_all(self):
        for i in range(2*self.num-1):
            self.eval(i)
    def update(self,a,b,x=None,f=None):
        """A[a]...A[b-1]をxに更新する
        """
        if f is None:
            # 更新クエリ
            f = lambda y: x
        k = 0
        q = [k] # k>=0なら行きがけ順
        # 重なる区間を深さ優先探索
        while q:
            k = q.pop()
            l,r = self.ls[k], self.rs[k]
            if k>=0:
                self.eval(k)
                if r<=a or b<=l:
                    continue
                elif a<=l and r<=b:
                    self.lazy[k] = composition(f, self.lazy[k])
                    self.eval(k)
                else:
                    q.append(~k)
                    q.append(2*k+1)
                    q.append(2*k+2)
            else:
                k = ~k
                self.seg[k] = op(self.seg[2*k+1], self.seg[2*k+2])
    def query(self,a,b):
        k = 0
        l = 0
        r = self.num
        q = [k]
        ans = ninf
        # 重なる区間を深さ優先探索
        while q:
            k = q.pop()
            l,r = self.ls[k], self.rs[k]
            self.eval(k)
            if r<=a or b<=l:
                continue
            elif a<=l and r<=b:
                ans = op(ans, self.seg[k])
            else:
                q.append(2*k+2)
                q.append(2*k+1)
#             print(q, ans, l,r,a,b, self.seg[k])
        return ans

n,q = list(map(int, input().split()))
M = 998244353
vals = [1]
vals2 = [1]
v = 1
for i in range(n):
    v *= 10
    v %= M
    vals.append(v)
    vals2.append((1+vals2[-1]*10)%M)
    
ninf = 0 # (転倒数, #0, #1)
V = (1<<32)
def op(x,y):
#     print(x,bin(y))
    return ((x%V)*vals[y//V] + y%V)%M + (x//V + y//V)*V
# op = lambda x,y: ((x[0]*vals[y[1]] + y[0])%M, x[1]+y[1])
# op = lambda x,y: ((x[0]*vals[y[1]] + y[0])%M, x[1]+y[1])

mapping = lambda f,x: x if f is None else ((f%10)*vals2[(x//V)-1])%M + (x//V)*V
def composition(f1,f2):
    if f1 is None:
        return f2
    elif f2 is None:
        return f1
    else:
        if f1//10<f2//10:
            return f2
        else:
            return f1
# composition = lambda f1, f2: f1 if f2 is None else 
f0 = None
sg = LazySegmentTree(n, [1+(1<<32)]*n)
ans = [None]*q
for i in range(q):
    l,r,d = map(int, input().split())
    l -= 1
    r -= 1
#     print(l,r,d)
    sg.update(l,r+1,f=10*i+d)
    ans[i] = sg.query(0,n+1)%V
write("\n".join(map(str, ans)))

Submission status: Accepted

Solution code:
mod = 998244353
eps = 10**-9


def main():
    import sys
    input = sys.stdin.buffer.readline

    beki10 = [0] * ((1 << 19) + 1)
    beki10[0] = 1
    for i in range(20):
        beki10[1 << i] = pow(10, (1 << i), mod)

    def op(a, b, seg_len):
        return (a * beki10[seg_len] + b)%mod

    e = 0
    inv9 = pow(9, mod - 2, mod)

    def mapping(a, x, seg_len):
        return ((x * inv9)%mod * (beki10[seg_len] - 1))%mod

    def composition(x, y):
        return y

    id = -1

    class LazySegTree:
        # Range update query
        def __init__(self, A, op=op, e=e, mapping=mapping, composition=composition, id=id, initialize=True):
            self.N = len(A)
            self.LV = (self.N - 1).bit_length()
            self.N0 = 1 << self.LV
            self.op = op
            self.e = e
            self.mapping = mapping
            self.composition = composition
            self.id = id
            if initialize:
                self.data = [self.e] * self.N0 + A + [self.e] * (self.N0 - self.N)
                seg_len = 1
                beki = self.N0 // 2
                for i in range(self.N0 - 1, 0, -1):
                    self.data[i] = op(self.data[i * 2], self.data[i * 2 + 1], seg_len)
                    if i == beki:
                        seg_len <<= 1
                        beki >>= 1
            else:
                self.data = [self.e] * (self.N0 * 2)
            self.lazy = [id] * (self.N0 * 2)

        def _ascend(self, i, seg_len):
            for _ in range(i.bit_length() - 1):
                i >>= 1
                self.data[i] = self.op(self.data[i * 2], self.data[i * 2 + 1], seg_len)
                seg_len <<= 1

        def _descend(self, idx):
            lv = idx.bit_length()
            seg_len = 1 << self.LV
            for j in range(lv - 1, 0, -1):
                seg_len >>= 1
                i = idx >> j
                x = self.lazy[i]
                if x == self.id:
                    continue
                self.lazy[i * 2] = self.composition(self.lazy[i * 2], x)
                self.lazy[i * 2 + 1] = self.composition(self.lazy[i * 2 + 1], x)
                self.lazy[i] = self.id
                self.data[i * 2] = self.mapping(self.data[i * 2], x, seg_len)
                self.data[i * 2 + 1] = self.mapping(self.data[i * 2 + 1], x, seg_len)

        # open interval [l, r)
        def apply(self, l, r, x):
            l += self.N0 - 1
            r += self.N0 - 1
            self._descend(l // (l & -l))
            self._descend(r // (r & -r) - 1)
            l_ori = l
            r_ori = r
            seg_len = 1
            while l < r:
                if l & 1:
                    self.data[l] = self.mapping(self.data[l], x, seg_len)
                    self.lazy[l] = self.composition(self.lazy[l], x)
                    l += 1
                if r & 1:
                    r -= 1
                    self.data[r] = self.mapping(self.data[r], x, seg_len)
                    self.lazy[r] = self.composition(self.lazy[r], x)
                l >>= 1
                r >>= 1
                seg_len <<= 1
            self._ascend(l_ori // (l_ori & -l_ori), l_ori & -l_ori)
            self._ascend(r_ori // (r_ori & -r_ori) - 1, r_ori & -r_ori)

        # open interval [l, r)
        def query(self, l, r):
            l += self.N0 - 1
            r += self.N0 - 1
            self._descend(l // (l & -l))
            self._descend(r // (r & -r) - 1)
            ret_l = self.e
            ret_r = self.e
            seg_len = 1
            while l < r:
                if l & 1:
                    ret_l = self.op(ret_l, self.data[l], seg_len)
                    l += 1
                if r & 1:
                    ret_r = self.op(self.data[r - 1], ret_r, seg_len)
                    r -= 1
                l >>= 1
                r >>= 1
                seg_len <<= 1
            return self.op(ret_l, ret_r, 1)

    N, Q = map(int, input().split())
    N0 = 1 << (N - 1).bit_length()
    ST = LazySegTree([0] * (N0-N) + [1] * N)
    inv10 = pow(10, mod-2, mod)
    ans = [0] * Q
    for q in range(Q):
        l, r, d = map(int, input().split())
        ST.apply(N0 - N + l, N0 - N + r + 1, d)
        ans[q] = (ST.query(N0 - N + 1, N0+1) * inv10)%mod
    print(*ans, sep="\n")


if __name__ == '__main__':
    main()

Submission status: Accepted

Solution code:
class LazySegmentTree:  # from https://atcoder.jp/contests/practice2/submissions/16598122
    __slots__ = ["n", "data", "lazy", "me", "oe", "fmm", "fmo", "foo"]

    def __init__(self, monoid_data, monoid_identity, operator_identity, func_monoid_monoid, func_monoid_operator,
                 func_operator_operator):
        self.me = monoid_identity
        self.oe = operator_identity
        self.fmm = func_monoid_monoid
        self.fmo = func_monoid_operator
        self.foo = func_operator_operator

        self.n = len(monoid_data)
        self.data = monoid_data*2
        for i in range(self.n-1, 0, -1):
            self.data[i] = self.fmm(self.data[2*i], self.data[2*i+1])
        self.lazy = [self.oe]*(self.n*2)

    def replace(self, index, value):
        index += self.n

        # propagation
        for shift in range(index.bit_length()-1, 0, -1):
            i = index>>shift
            self.lazy[2*i] = self.foo(self.lazy[2*i], self.lazy[i])
            self.lazy[2*i+1] = self.foo(self.lazy[2*i+1], self.lazy[i])
            self.data[i] = self.fmo(self.data[i], self.lazy[i])
            self.lazy[i] = self.oe

        # update
        self.data[index] = value
        self.lazy[index] = self.oe

        # recalculation
        i = index
        while i > 1:
            i //= 2
            self.data[i] = self.fmm(self.fmo(self.data[2*i], self.lazy[2*i]),
                                    self.fmo(self.data[2*i+1], self.lazy[2*i+1]))
            self.lazy[i] = self.oe

    def effect(self, l, r, operator):
        l += self.n
        r += self.n

        # preparing indices
        indices = []
        l0 = (l//(l&-l)) >> 1
        r0 = (r//(r&-r)-1) >> 1
        while r0 > l0:
            indices.append(r0)
            r0 >>= 1
        while l0 > r0:
            indices.append(l0)
            l0 >>= 1
        while l0 and l0 != r0:
            indices.append(r0)
            r0 >>= 1
            if l0 == r0:
                break
            indices.append(l0)
            l0 >>= 1
        while r0:
            indices.append(r0)
            r0 >>= 1

        # propagation
        for i in reversed(indices):
            self.lazy[2*i] = self.foo(self.lazy[2*i], self.lazy[i])
            self.lazy[2*i+1] = self.foo(self.lazy[2*i+1], self.lazy[i])
            self.data[i] = self.fmo(self.data[i], self.lazy[i])
            self.lazy[i] = self.oe

        # effect
        while l < r:
            if l&1:
                self.lazy[l] = self.foo(self.lazy[l], operator)
                l += 1
            if r&1:
                r -= 1
                self.lazy[r] = self.foo(self.lazy[r], operator)
            l >>= 1
            r >>= 1

        # recalculation
        for i in indices:
            self.data[i] = self.fmm(self.fmo(self.data[2*i], self.lazy[2*i]),
                                    self.fmo(self.data[2*i+1], self.lazy[2*i+1]))
            self.lazy[i] = self.oe

    def folded(self, l, r):
        l += self.n
        r += self.n

        # preparing indices
        indices = []
        l0 = (l//(l&-l))//2
        r0 = (r//(r&-r)-1)//2
        while r0 > l0:
            indices.append(r0)
            r0 >>= 1
        while l0 > r0:
            indices.append(l0)
            l0 >>= 1
        while l0 and l0 != r0:
            indices.append(r0)
            r0 >>= 1
            if l0 == r0:
                break
            indices.append(l0)
            l0 >>= 1
        while r0:
            indices.append(r0)
            r0 >>= 1

        # propagation
        for i in reversed(indices):
            self.lazy[2*i] = self.foo(self.lazy[2*i], self.lazy[i])
            self.lazy[2*i+1] = self.foo(self.lazy[2*i+1], self.lazy[i])
            self.data[i] = self.fmo(self.data[i], self.lazy[i])
            self.lazy[i] = self.oe

        # fold
        left_folded = self.me
        right_folded = self.me
        while l < r:
            if l&1:
                left_folded = self.fmm(left_folded, self.fmo(self.data[l], self.lazy[l]))
                l += 1
            if r&1:
                r -= 1
                right_folded = self.fmm(self.fmo(self.data[r], self.lazy[r]), right_folded)
            l >>= 1
            r >>= 1
        return self.fmm(left_folded, right_folded)

import sys

mod = 998244353
p = 1
n = 0
p10 = []
n11 = []
for i in range(202020):
    p10.append(p)
    n11.append(n)
    p = p * 10 % mod
    n = (n * 10 + 1) % mod

def op(a, b):
    a_val, a_siz = a
    b_val, b_siz = b
    siz = a_siz + b_siz
    val = a_val * p10[b_siz] + b_val
    return val % mod, siz

e = (0, 0)

def mapping(a, f):
    if f == 0:
        return a
    siz = a[1]
    return f * n11[siz] % mod, siz

aaa = lambda g, f: f if f != 0 else g

i = 0

N, Q = map(int, sys.stdin.buffer.readline().split())

#seg = LazySegTree(op, e, mapping, aaa, i, [(1, 1) for _ in range(N)])
seg = LazySegmentTree([(1, 1) for _ in range(N)], e, i, op, mapping, aaa)

LRD = [list(map(int, input().split())) for _ in range(Q)]
Ans = []
for l, r, d in LRD:
    #seg.apply(l-1, r, d)
    seg.effect(l-1, r, d)
    #ans = seg.all_prod()[0]
    ans = seg.folded(0, N)[0]
    Ans.append(ans)
print("\n".join(map(str, Ans)))

Submission status: Accepted

Solution code:
class LazySegmentTree:
    def __init__(self, op_X, e_X, mapping, compose, id_M, N, array=None):
        __slots__ = ["op_X","e_X","mapping","compose","id_M","N","log","N0","data","lazy"]
        #  それぞれ Xの演算、単位元、f(x),  f\circ g, Xの恒等変換
        self.e_X = e_X; self.op_X = op_X; self.mapping = mapping; self.compose = compose; self.id_M = id_M
        self.N = N
        self.log = (N-1).bit_length()
        self.N0 = 1<<self.log
        self.data = [e_X]*(2*self.N0)
        self.lazy = [id_M]*self.N0
        if array is not None:
            assert N == len(array)
            self.data[self.N0:self.N0+self.N] = array
            for i in range(self.N0-1,0,-1): self.update(i)

    # 1点更新
    def point_set(self, p, x):
        p += self.N0
        for i in range(self.log, 0,-1):
            self.push(p>>i)
        self.data[p] = x
        for i in range(1, self.log + 1):
            self.update(p>>i)
 
    # 1点取得
    def point_get(self, p):
        p += self.N0
        for i in range(self.log, 0, -1):
            self.push(p>>i)
        return self.data[p]
 
    # 半開区間[L,R)をopでまとめる
    def prod(self, l, r):
        if l == r: return self.e_X
        l += self.N0
        r += self.N0
        for i in range(self.log, 0, -1):
            if (l>>i)<<i != l:
                self.push(l>>i)
            if (r>>i)<<i != r:
                self.push(r>>i)

        sml = smr = self.e_X
        while l < r:
            if l & 1: 
                sml = self.op_X(sml, self.data[l])
                l += 1
            if r & 1:
                r -= 1
                smr = self.op_X(self.data[r], smr)
            l >>= 1
            r >>= 1
        return self.op_X(sml, smr)
 
    # 全体をopでまとめる
    def all_prod(self): return self.data[1]
 
    # 1点作用
    def apply(self, p, f):
        p += self.N0
        for i in range(self.log, 0, -1):
            self.push(p>>i)
        self.data[p] = self.mapping(f, self.data[p])
        for i in range(1, self.log + 1):
            self.update(p>>i)
 
    # 区間作用
    def apply(self, l, r, f):
        if l == r: return
        l += self.N0
        r += self.N0
        for i in range(self.log, 0, -1):
            if (l>>i)<<i != l:
                self.push(l>>i)
            if (r>>i)<<i != r:
                self.push((r-1)>>i)

        l2, r2 = l, r
        while l < r:
            if l & 1: 
                self.all_apply(l, f)
                l += 1
            if r & 1:
                r -= 1
                self.all_apply(r, f)
            l >>= 1
            r >>= 1

        l, r = l2, r2
        for i in range(1, self.log + 1):
            if (l>>i)<<i != l:
                self.update(l>>i)
            if (r>>i)<<i != r:
                self.update((r-1)>>i)
     
    """
    始点 l を固定
    f(x_l*...*x_{r-1}) が True になる最大の r 
    つまり TTTTFFFF となるとき、F となる最小の添え字
    存在しない場合 n が返る
    f(e_M) = True でないと壊れる
    """
    def max_right(self, l, g):
        if l == self.N: return self.N
        l += self.N0
        for i in range(self.log, 0, -1): self.push(l>>i)
        sm = self.e_X
        while True:
            while l&1 == 0:
                l >>= 1
            if not g(self.op_X(sm, self.data[l])):
                while l < self.N0:
                    self.push(l)
                    l *= 2
                    if g(self.op_X(sm, self.data[l])):
                        sm = self.op_X(sm, self.data[l])
                        l += 1
                return l - self.N0
            sm = self.op_X(sm, self.data[l])
            l += 1
            if l&-l == l: break
        return self.N
 
    """
    終点 r を固定
    f(x_l*...*x_{r-1}) が True になる最小の l
    つまり FFFFTTTT となるとき、T となる最小の添え字
    存在しない場合 r が返る
    f(e_M) = True でないと壊れる
    """
    def min_left(self, r, g):
        if r == 0: return 0
        r += self.N0
        for i in range(self.log, 0, -1): self.push((r-1)>>i)
        sm = self.e_X
        while True:
            r -= 1
            while r>1 and r&1:
                r >>= 1
            if not g(self.op_X(self.data[r], sm)):
                while r < self.N0:
                    self.push(r)
                    r = 2*r + 1
                    if g(self.op_X(self.data[r], sm)):
                        sm = self.op_X(self.data[r], sm)
                        r -= 1
                return r + 1 - self.N0
            sm = self.op_X(self.data[r], sm)
            if r&-r == r: break
        return 0
        
    # 以下内部関数
    def update(self, k):
        self.data[k] = self.op_X(self.data[2*k], self.data[2*k+1])
    
    def all_apply(self, k, f):
        self.data[k] = self.mapping(f, self.data[k])
        if k < self.N0:
            self.lazy[k] = self.compose(f, self.lazy[k])

    def push(self, k): #propagate と同じ
        if self.lazy[k] is self.id_M: return
        self.data[2*k  ] = self.mapping(self.lazy[k], self.data[2*k])
        self.data[2*k+1] = self.mapping(self.lazy[k], self.data[2*k+1])
        if 2*k < self.N0:
            self.lazy[2*k]   = self.compose(self.lazy[k], self.lazy[2*k])
            self.lazy[2*k+1] = self.compose(self.lazy[k], self.lazy[2*k+1])
        self.lazy[k] = self.id_M

###################################################################

e_X = 0
id_M = 0
def op_X(X,Y):
    x1,d1 = X>>32,X&MASK
    x2,d2 = Y>>32,Y&MASK
    return (((x1*p10[d2]+x2)%MOD)<<32) + d1+d2    

def compose(f,g):
    return f

def mapping(f,X):
    if f==0:
        return X
    else:
        x,d = X>>32, X&MASK
        return (((p10[d]-1)*inv9*f%MOD)<<32) + d
        


import sys
readline = sys.stdin.readline

MOD = 998244353
MASK = (1<<32) - 1
inv9 = 443664157

n,q = map(int, readline().split())

p10 = [1]*(n+2)
for i in range(1,n+2):
    p10[i] = p10[i-1]*10%MOD

a = [(1<<32) + 1]*n
seg = LazySegmentTree(op_X, e_X, mapping, compose, id_M, n, array=a)

#print(seg.all_prod()>>32)
#print([i>>32 for i in seg.data])

for _ in range(q):
    l,r,d = map(int, readline().split())
    seg.apply(l-1,r,d)
    print(seg.all_prod()>>32)

Submission status: Accepted

Solution code:
class LazySegmentTree():
    __slots__ = ["merge","merge_unit","operate","merge_operate","operate_unit","n","data","lazy"]
    def __init__(self,n,init,merge,merge_unit,operate,merge_operate,operate_unit):
        self.merge=merge
        self.merge_unit=merge_unit
        self.operate=operate
        self.merge_operate=merge_operate
        self.operate_unit=operate_unit

        self.n=(n-1).bit_length()
        self.data=[merge_unit for i in range(1<<(self.n+1))]
        self.lazy=[operate_unit for i in range(1<<(self.n+1))]
        if init:
            for i in range(n):
                self.data[2**self.n+i]=init[i]
            for i in range(2**self.n-1,0,-1):
                self.data[i]=self.merge(self.data[2*i],self.data[2*i+1])

    def propagate(self,v):
        ope = self.lazy[v]
        if ope == self.operate_unit:
            return
        self.lazy[v]=self.operate_unit
        self.data[(v<<1)]=self.operate(self.data[(v<<1)],ope)
        self.data[(v<<1)+1]=self.operate(self.data[(v<<1)+1],ope)
        self.lazy[v<<1]=self.merge_operate(self.lazy[(v<<1)],ope)
        self.lazy[(v<<1)+1]=self.merge_operate(self.lazy[(v<<1)+1],ope)

    def propagate_above(self,i):
        m=i.bit_length()-1
        for bit in range(m,0,-1):
            v=i>>bit
            self.propagate(v)

    def remerge_above(self,i):
        while i:
            c = self.merge(self.data[i],self.data[i^1])
            i>>=1
            self.data[i]=self.operate(c,self.lazy[i])

    def update(self,l,r,x):
        l+=1<<self.n
        r+=1<<self.n
        l0=l//(l&-l)
        r0=r//(r&-r)-1
        self.propagate_above(l0)
        self.propagate_above(r0)
        while l<r:
            if l&1:
                self.data[l]=self.operate(self.data[l],x)
                self.lazy[l]=self.merge_operate(self.lazy[l],x)
                l+=1
            if r&1:
                self.data[r-1]=self.operate(self.data[r-1],x)
                self.lazy[r-1]=self.merge_operate(self.lazy[r-1],x)
            l>>=1
            r>>=1
        self.remerge_above(l0)
        self.remerge_above(r0)

    def query(self,l,r):
        l+=1<<self.n
        r+=1<<self.n
        l0=l//(l&-l)
        r0=r//(r&-r)-1
        self.propagate_above(l0)
        self.propagate_above(r0)
        res=self.merge_unit
        while l<r:
            if l&1:
                res=self.merge(res,self.data[l])
                l+=1
            if r&1:
                res=self.merge(res,self.data[r-1])
            l>>=1
            r>>=1
        return res

    def bisect_l(self,l,r,x):
        l += 1<<self.n
        r += 1<<self.n
        l0=l//(l&-l)
        r0=r//(r&-r)-1
        self.propagate_above(l0)
        self.propagate_above(r0)
        Lmin = -1
        Rmin = -1
        while l<r:
            if l & 1:
                if self.data[l][0] <= x and Lmin==-1:
                    Lmin = l
                l += 1
            if r & 1:
                if self.data[r-1][0] <=x:
                    Rmin = r-1
            l >>= 1
            r >>= 1

        res = -1
        if Lmin != -1:
            pos = Lmin
            while pos<self.num:
                self.propagate(pos)
                if self.data[2 * pos][0] <=x:
                    pos = 2 * pos
                else:
                    pos = 2 * pos +1
            res =  pos-self.num
        if Rmin != -1:
            pos = Rmin
            while pos<self.num:
                self.propagate(pos)
                if self.data[2 * pos][0] <=x:
                    pos = 2 * pos
                else:
                    pos = 2 * pos +1
            if res==-1:
                res = pos-self.num
            else:
                res = min(res,pos-self.num)

        return res

mod = 998244353
mask = (2**32)-1

def merge(x,y):
    val_x,base_x = x>>32,x&mask
    val_y,base_y = y>>32,y&mask

    res_val = (val_x+val_y) % mod
    res_base = (base_x+base_y) % mod
    res = (res_val<<32) + res_base
    return res

merge_unit = 0

def operate(x,y):
    if y==0:
        return x
    val,base = x>>32,x&mask
    val = (base*y) % mod
    x = (val<<32)+base
    return x

def merge_operate(x,y):
    return y

operate_unit = 0

import sys

input = sys.stdin.readline


N,Q = map(int,input().split())
init = [(pow(10,N-1-i,mod)<<32)+pow(10,N-1-i,mod) for i in range(N)]
LST = LazySegmentTree(N,init,merge,merge_unit,operate,merge_operate,operate_unit)

for i in range(Q):
    l,r,d = map(int,input().split())
    LST.update(l-1,r,d)
    print(LST.query(0,N)>>32)

Submission status: Accepted

Solution code:
import sys; input = sys.stdin.buffer.readline
sys.setrecursionlimit(10**7)
from collections import defaultdict
mod = 998244353; INF = float("inf")

def getlist():
	return list(map(int, input().split()))

def inverse(N, mod):
	return (pow(N, mod - 2, mod))

class lazySegTree(object):
	def __init__(self, N):
		self.N = N
		self.LV = (N - 1).bit_length()
		self.N0 = 2 ** self.LV
		self.data = [0] * (2 * self.N0)
		self.lazy = [0] * (2 * self.N0)
		self.size = [1] * (2 * self.N0)
		for i in range(self.N):
			self.size[self.N0 - 1 + i] = pow(10, self.N - i - 1 , mod)
		for i in range(self.N0 - 2, -1, -1):
			self.size[i] = (self.size[2 * i + 1] + self.size[2 * i + 2]) % mod

	# 遅延セグ木の初期化
	def initialize(self):
		for i in range(self.N):
			self.data[self.N0 - 1 + i] = pow(10, self.N - i - 1, mod)
		for i in range(self.N0 - 2, -1, -1):
			self.data[i] = (self.data[2 * i + 1] + self.data[2 * i + 2]) % mod

	# 遅延伝播を行うindexを生成
	def gindex(self, l, r):
		L = (l + self.N0) >> 1; R = (r + self.N0) >> 1
		lc = 0 if l & 1 else (L & -L).bit_length()
		rc = 0 if r & 1 else (R & -R).bit_length()
		for i in range(self.LV):
			if rc <= i:
				yield R
			if L < R and lc <= i:
				yield L
			L >>= 1; R >>= 1

	# 遅延伝搬処理
	def propagates(self, *ids):
		for i in reversed(ids):
			v = self.lazy[i - 1]
			if not v:
				continue
			self.lazy[2 * i - 1] = v; self.lazy[2 * i] = v
			self.data[2 * i - 1] = v * self.size[2 * i - 1]
			self.data[2 * i] = v * self.size[2 * i]
			self.lazy[i - 1] = 0

	def update(self, l, r, x):
		*ids, = self.gindex(l, r + 1)
		self.propagates(*ids)

		L = self.N0 + l; R = self.N0 + r + 1
		while L < R:
			if R & 1:
				R -= 1
				self.lazy[R - 1] = x; self.data[R - 1] = x * self.size[R - 1]
			if L & 1:
				self.lazy[L - 1] = x; self.data[L - 1] = x * self.size[L - 1]
				L += 1
			L >>= 1; R >>= 1
		for i in ids:
			self.data[i - 1] = (self.data[2 * i - 1] + self.data[2 * i]) % mod

	# 区間[l, r]内の和を求める
	def query(self, l, r):
		self.propagates(*self.gindex(l, r + 1))
		L = self.N0 + l; R = self.N0 + r + 1
		s = 0
		while L < R:
			if R & 1:
				R -= 1
				s += self.data[R - 1]
			if L & 1:
				s += self.data[L - 1]
				L += 1
			L >>= 1; R >>= 1
			s %= mod
		return s

	# デバッグ用
	def debug(self):
		ids = [i for i in range(self.N0 - 1, 0, -1)]
		self.propagates(*ids)
		print(self.data[self.N0 - 1:])

def main():
	N, Q = getlist()
	lSeg = lazySegTree(N)
	lSeg.initialize()
	# lSeg.debug()
	for i in range(Q):
		L, R, D = getlist()
		L -= 1; R -= 1
		lSeg.update(L, R, D)
		ans = lSeg.query(0, N - 1)
		print(ans)





if __name__ == '__main__':
	main()