Problem:
 Score : 500 points    Problem Statement You have a string S of length N .
Initially, all characters in S are 1 s.  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.

Solution (Python):
import sys
sys.setrecursionlimit(10**7)
def MI(): return map(int,sys.stdin.readline().rstrip().split())


N,Q = MI()
mod = 998244353
power = [1]  
for i in range(N):
    power.append((power[-1]*10) % mod)
t = pow(9,mod-2,mod)  


class LazySegTree():  
    def __init__(self,N,X_func,A_func,operate,X_unit,A_unit):
        self.N = N
        self.X_func = X_func
        self.A_func = A_func
        self.operate = operate
        self.X_unit = X_unit
        self.A_unit = A_unit
        self.X = [self.X_unit]*(2*self.N)
        self.A = [self.A_unit]*(2*self.N)
        self.size = [0]*(2*self.N)

    def build(self,init_value):  
        for i in range(self.N):
            self.X[self.N+i] = init_value[i]
            self.size[self.N+i] = 1
        for i in range(self.N-1,0,-1):
            self.X[i] = self.X_func(self.X[i << 1],self.X[i << 1 | 1])
            self.size[i] = self.size[i << 1] + self.size[i << 1 | 1]

    def update(self,i,x):  
        i += self.N
        self.X[i] = x
        i >>= 1
        while i:
            self.X[i] = self.X_func(self.X[i << 1],self.X[i << 1 | 1])
            i >>= 1

    def eval_at(self,i):  
        return self.operate(self.X[i],self.A[i],self.size[i])

    def eval_above(self,i):  
        i >>= 1
        while i:
            self.X[i] = self.X_func(self.eval_at(i << 1),self.eval_at(i << 1 | 1))
            i >>= 1

    def propagate_at(self,i):  
        self.X[i] = self.operate(self.X[i],self.A[i],self.size[i])
        self.A[i << 1] = self.A_func(self.A[i << 1],self.A[i])
        self.A[i << 1 | 1] = self.A_func(self.A[i << 1 | 1], self.A[i])
        self.A[i] = self.A_unit

    def propagate_above(self,i):  
        H = i.bit_length()
        for h in range(H,0,-1):
            self.propagate_at(i >> h)

    def fold(self,L,R):  
        L += self.N
        R += self.N
        L0 = L // (L & -L)
        R0 = R // (R & -R) - 1
        self.propagate_above(L0)
        self.propagate_above(R0)
        vL = self.X_unit
        vR = self.X_unit
        while L < R:
            if L & 1:
                vL = self.X_func(vL,self.eval_at(L))
                L += 1
            if R & 1:
                R -= 1
                vR = self.X_func(self.eval_at(R),vR)
            L >>= 1
            R >>= 1
        return self.X_func(vL,vR)

    def operate_range(self,L,R,x):  
        L += self.N
        R += 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.A[L] = self.A_func(self.A[L],x)
                L += 1
            if R & 1:
                R -= 1
                self.A[R] = self.A_func(self.A[R],x)
            L >>= 1
            R >>= 1
        self.eval_above(L0)
        self.eval_above(R0)


def X_func(x,y):
    x1,x2 = x >> 18,x & ((1 << 18)-1)
    y1,y2 = y >> 18,y & ((1 << 18)-1)
    return ((((x1*power[y2]+y1) % mod) << 18)+x2+y2)


def A_func(a,b):
    if b == 0:
        return a
    return b


def operate(x,a,r):  
    if a == 0:
        return x
    x1,x2 = x >> 18,x & ((1 << 18)-1)
    return ((((a*(power[x2]-1)*t) % mod) << 18)+x2)


LST = LazySegTree(N,X_func,A_func,operate,0,0)
LST.build([(1 << 18)+1]*N)

for _ in range(Q):
    l,r,d = MI()
    LST.operate_range(l-1,r,d)
    print(LST.fold(0,N) >> 18)

Solution (Python):


import sys

readline = sys.stdin.buffer.readline


INF = 1 << 60


def read_int():
    return int(readline())


def read_int_n():
    return list(map(int, readline().split()))


def read_float():
    return float(readline())


def read_float_n():
    return list(map(float, readline().split()))


def read_str():
    return readline().strip()


def read_str_n():
    return readline().strip().split()


def ep(*args):
    print(*args, file=sys.stderr)


def mt(f):
    import time

    def wrap(*args, **kwargs):
        s = time.perf_counter()
        ret = f(*args, **kwargs)
        e = time.perf_counter()

        ep(e - s, 'sec')
        return ret

    return wrap


class LazySegmentTree():
    def __init__(self, op, e, mapping, composition, im, init_array):
        self.op = op
        self.e = e
        self.mapping = mapping
        self.composition = composition
        self.im = im

        l = len(init_array)

        def ceil_pow2(n):
            x = 0
            while (1 << x) < n:
                x += 1
            return x
        self.log = ceil_pow2(l)
        self.size = 1 << self.log
        self.d = [e() for _ in range(2*self.size)]
        self.lz = [im() for _ in range(self.size)]

        for i, a in enumerate(init_array):
            self.d[i+self.size] = a

        for i in range(self.size-1, 0, -1):
            self.__update(i)

    def set(self, p, x):
        p += self.size

        for i in range(self.log, 0, -1):
            self.__push(p >> i)

        self.d[p] = x

        for i in range(1, self.log+1):
            self.__update(p >> i)

    def __getitem__(self, p):
        p += self.size
        for i in range(self.log, 0, -1):
            self.__push(p >> i)
        return self.d[p]

    def prod(self, l, r):
        if l == r:
            return self.e()

        l += self.size
        r += self.size

        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 = self.e()
        smr = self.e()

        while l < r:
            if l & 1:
                sml = self.op(sml, self.d[l])
                l += 1
            if r & 1:
                r -= 1
                smr = self.op(self.d[r], smr)
            l >>= 1
            r >>= 1

        return self.op(sml, smr)

    def apply(self, l, r, f):
        if l == r:
            return

        l += self.size
        r += self.size

        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)

    def __update(self, k):
        self.d[k] = self.op(self.d[2*k], self.d[2*k+1])

    def __all_apply(self, k, f):
        self.d[k] = self.mapping(f, self.d[k])
        if k < self.size:
            self.lz[k] = self.composition(f, self.lz[k])

    def __push(self, k):
        self.__all_apply(2*k, self.lz[k])
        self.__all_apply(2*k+1, self.lz[k])
        self.lz[k] = self.im()

M = 998244353
m = 1 << 32
m -= 1

def ser(a, b):
    return (a << 32) + b

def des(v):
    return v >> 32, v & m

def e():
    return 0

def op(sl, sr):
    sl = des(sl)
    sr = des(sr)
    return ser((sl[0] + sr[0]) % M, (sl[1] + sr[1]) % M)

def mapping(fl, sr):
    if fl == 0:
        return sr
    sr = des(sr)
    return ser((sr[1]*fl) % M, sr[1])

def composition(fl, fr):
    if fl == 0:
        return fr
    return fl

def im():
    return 0

@mt
def slv(N, Q, LRQ):
    A = []
    for i in range(N):
        v = pow(10, N-i-1, M)
        v %= M
        A.append(ser(v, v))
    st = LazySegmentTree(op, e, mapping, composition, im, A)

    ans = []
    for i, (l, r, q) in enumerate(LRQ, start=1):
        l -= 1
        st.apply(l, r, q)
        ans.append(des(st.prod(0, N))[0])
    return ans


def main():
    N, Q = read_int_n()
    LRQ = [read_int_n() for _ in range(Q)]
    print(*slv(N, Q, LRQ), sep='\n')


if __name__ == '__main__':
    main()

Solution (Python):
import sys
sys.setrecursionlimit(10**7)
def MI(): return map(int,sys.stdin.readline().rstrip().split())


N,Q = MI()
mod = 998244353
power = [1]  
for i in range(N):
    power.append((power[-1]*10) % mod)
t = pow(9,mod-2,mod)  


class LazySegTree():  
    def __init__(self,N,X_func,A_func,operate,X_unit,A_unit):
        self.N = N
        self.X_func = X_func
        self.A_func = A_func
        self.operate = operate
        self.X_unit = X_unit
        self.A_unit = A_unit
        self.X = [self.X_unit]*(2*self.N)
        self.A = [self.A_unit]*(2*self.N)
        self.size = [0]*(2*self.N)

    def build(self,init_value):  
        for i in range(self.N):
            self.X[self.N+i] = init_value[i]
            self.size[self.N+i] = 1
        for i in range(self.N-1,0,-1):
            self.X[i] = self.X_func(self.X[i << 1],self.X[i << 1 | 1])
            self.size[i] = self.size[i << 1] + self.size[i << 1 | 1]

    def update(self,i,x):  
        i += self.N
        self.X[i] = x
        i >>= 1
        while i:
            self.X[i] = self.X_func(self.X[i << 1],self.X[i << 1 | 1])
            i >>= 1

    def eval_at(self,i):  
        return self.operate(self.X[i],self.A[i],self.size[i])

    def eval_above(self,i):  
        i >>= 1
        while i:
            self.X[i] = self.X_func(self.eval_at(i << 1),self.eval_at(i << 1 | 1))
            i >>= 1

    def propagate_at(self,i):  
        self.X[i] = self.operate(self.X[i],self.A[i],self.size[i])
        self.A[i << 1] = self.A_func(self.A[i << 1],self.A[i])
        self.A[i << 1 | 1] = self.A_func(self.A[i << 1 | 1], self.A[i])
        self.A[i] = self.A_unit

    def propagate_above(self,i):  
        H = i.bit_length()
        for h in range(H,0,-1):
            self.propagate_at(i >> h)

    def fold(self,L,R):  
        L += self.N
        R += self.N
        L0 = L // (L & -L)
        R0 = R // (R & -R) - 1
        self.propagate_above(L0)
        self.propagate_above(R0)
        vL = self.X_unit
        vR = self.X_unit
        while L < R:
            if L & 1:
                vL = self.X_func(vL,self.eval_at(L))
                L += 1
            if R & 1:
                R -= 1
                vR = self.X_func(self.eval_at(R),vR)
            L >>= 1
            R >>= 1
        return self.X_func(vL,vR)

    def operate_range(self,L,R,x):  
        L += self.N
        R += 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.A[L] = self.A_func(self.A[L],x)
                L += 1
            if R & 1:
                R -= 1
                self.A[R] = self.A_func(self.A[R],x)
            L >>= 1
            R >>= 1
        self.eval_above(L0)
        self.eval_above(R0)


def X_func(x,y):
    x1,x2 = x
    y1,y2 = y
    return ((x1*power[y2]+y1) % mod,x2+y2)


def A_func(a,b):
    if b == 0:
        return a
    return b


def operate(x,a,r):  
    if a == 0:
        return x
    x1,x2 = x
    return ((a*(power[x2]-1)*t) % mod,x2)


LST = LazySegTree(N,X_func,A_func,operate,(0,0),0)
LST.build([(1,1)]*N)

for _ in range(Q):
    l,r,d = MI()
    LST.operate_range(l-1,r,d)
    print(LST.fold(0,N)[0])

Solution (Python):
import sys
input = sys.stdin.readline
N, Q = map(int, input().split())
mod = 998244353
class lazy_segtree:
  
  def __init__(s, op, e, mapping, composition, id, v):
    if type(v) is int: v = [e()] * v
    s._n = len(v)
    s.log = s.ceil_pow2(s._n)
    s.size = 1 << s.log
    s.d = [e()] * (2 * s.size)
    s.lz = [id()] * s.size
    s.e = e
    s.op = op
    s.mapping = mapping
    s.composition = composition
    s.id = id
    for i in range(s._n): s.d[s.size + i] = v[i]
    for i in range(s.size - 1, 0, -1): s.update(i)
  
  
  def set(s, p, x):
    p += s.size
    for i in range(s.log, 0, -1): s.push(p >> i)
    s.d[p] = x
    for i in range(1, s.log + 1): s.update(p >> i)
 
  
  def get(s, p):
    p += s.size
    for i in range(s.log, 0, -1): s.push(p >> i)
    return s.d[p]
 
  
  def prod(s, l, r):
    if l == r: return s.e()
    l += s.size
    r += s.size
    for i in range(s.log, 0, -1):
      if (((l >> i) << i) != l): s.push(l >> i)
      if (((r >> i) << i) != r): s.push(r >> i)
    sml, smr = s.e(), s.e()
    while (l < r):
      if l & 1: 
        sml = s.op(sml, s.d[l])
        l += 1
      if r & 1:
        r -= 1
        smr = s.op(s.d[r], smr)
      l >>= 1
      r >>= 1
    return s.op(sml, smr)
 
  
  def all_prod(s): return s.d[1]
 
  
  def apply(s, p, f):
    p += s.size
    for i in range(s.log, 0, -1): s.push(p >> i)
    s.d[p] = s.mapping(f, s.d[p])
    for i in range(1, s.log + 1): s.update(p >> i)
 
  
  def apply(s, l, r, f):
    if l == r: return
    l += s.size
    r += s.size
    for i in range(s.log, 0, -1):
      if (((l >> i) << i) != l): s.push(l >> i)
      if (((r >> i) << i) != r): s.push((r - 1) >> i)
    l2, r2 = l, r
    while l < r:
      if l & 1: 
        sml = s.all_apply(l, f)
        l += 1
      if r & 1:
        r -= 1
        smr = s.all_apply(r, f)
      l >>= 1
      r >>= 1
    l, r = l2, r2
    for i in range(1, s.log + 1):
      if (((l >> i) << i) != l): s.update(l >> i)
      if (((r >> i) << i) != r): s.update((r - 1) >> i)
   
  
  def max_right(s, l, g):
    if l == s._n: return _n
    l += s.size
    for i in range(s.log, 0, -1): s.push(l >> i)
    sm = s.e()
    while True:
      while (l % 2 == 0): l >>= 1
      if not g(s.op(sm, s.d[l])):
        while l < s.size:
          s.push(l)
          l = 2 * l
          if g(s.op(sm, s.d[l])):
            sm = s.op(sm, s.d[l])
            l += 1
        return l - s.size
      sm = s.op(sm, s.d[l])
      l += 1
      if (l & -l) == l: break
    return s._n
 
  
  def min_left(s, r, g):
    if r == 0: return 0
    r += s.size
    for i in range(s.log, 0, -1): s.push((r - 1) >> i)
    sm = s.e()
    while True:
      r -= 1
      while r > 1 and (r % 2): r >>= 1
      if not g(s.op(s.d[r], sm)):
        while r < s.size:
          s.push(r)
          r = 2 * r + 1
          if g(s.op(s.d[r], sm)):
            sm = s.op(s.d[r], sm)
            r -= 1
        return r + 1 - s.size
      sm = s.op(s.d[r], sm)
      if (r & - r) == r: break
    return 0
 
  def update(s, k): s.d[k] = s.op(s.d[2 * k], s.d[2 * k + 1])
  def all_apply(s, k, f):
    s.d[k] = s.mapping(f, s.d[k])
    if k < s.size: s.lz[k] = s.composition(f, s.lz[k])
  def push(s, k):
    s.all_apply(2 * k, s.lz[k])
    s.all_apply(2 * k + 1, s.lz[k])
    s.lz[k] = s.id()
  def ceil_pow2(s, n):
    x = 0
    while (1 << x) < n: x += 1
    return x
 
inf = 10 ** 18
def e():
  return 0
 
def op(x, y):
  x0, x1 = x >> 32, x % (1 << 32)
  y0, y1 = y >> 32, y % (1 << 32)
  return (((x0 + y0) % mod) << 32) + (x1 + y1) % mod
 
def mapping(f, x):
  x0, x1 = x >> 32, x % (1 << 32)
  if f != inf: return ((f * x1 % mod) << 32) + x1
  else: return x
 
def composition(f, g):
  if f != inf: return f
  else: return g
 
def id(x = inf): return x

a = [pow(10, N - i - 1, mod) for i in range(N)]
seg = lazy_segtree(op, e, mapping, composition, id, [(x << 32) + x for x in a])

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

Solution (Python):
header_code = """






namespace aclython {

using mint = atcoder::modint998244353;

const mint inv_nine = mint(1)/mint(9);

struct S {
    mint a;
    mint b;
    S(mint _a, mint _b) : a(_a) ,b(_b) {}
    S(int _a, int _b) : a(_a) ,b(_b) {}
    S(const S &s) : a(s.a), b(s.b) {}
    int get_a() { return (int)a.val(); }
};

struct F {
    mint a;
    F(mint _a) : a(_a) {}
    F(int _a) : a(_a) {}
};


S op(S l, S r) { return S{l.a * r.b + r.a, l.b * r.b}; }

S e() { return S{0, 1}; }

S mapping(F l, S r) {
    if (l.a==0) return r;
    return S{l.a * (r.b - 1)*inv_nine, r.b};
    }

F composition(F l, F r) {
    if (l.a==0) return r;
    return l;
    }

F id() { return F{0}; }

struct lazy_segtree{
    lazy_segtree() : lazy_segtree(0) {}
    lazy_segtree(int n) : lazy_segtree(std::vector<S>(n, e())) {}
    lazy_segtree(const std::vector<S>& vec) : seg(vec) {}
    void set(int p, S x) { seg.set(p, x); }
    S get(int p) { return seg.get(p); }
    S prod(int l, int r) { return seg.prod(l, r); }
    S all_prod() { return seg.all_prod(); }
    void apply(int p, F f) { seg.apply(p, f); }
    void apply(int l, int r, F f) { seg.apply(l, r, f); }
    private:
        atcoder::lazy_segtree<S, op, e, F, mapping, composition, id> seg;
};

}
"""

code = """






from libcpp.string cimport string
from libcpp.vector cimport vector
from libc.stdio cimport getchar, printf
from libcpp cimport bool

cdef extern from "./intermediate.hpp" namespace "aclython" nogil:
    cdef cppclass S:
        S(int, int)
        S(S &)
        int get_a()
    cdef cppclass F:
        F(int)
    cdef cppclass lazy_segtree:
        lazy_segtree(vector[S] v)
        void set(int p, S x)
        S get(int p)
        S prod(int l, int r)
        S all_prod()
        void apply(int p, F f)
        void apply(int l, int r, F f)

cdef class LazySegTree:
    cdef lazy_segtree *_thisptr
    def __cinit__(self, vector[vector[int]] v):
        cdef int n = v.size()
        cdef vector[S] *sv = new vector[S]()
        cdef S *s
        for i in range(n):
            s = new S(v.at(i).at(0), v.at(i).at(1))
            sv.push_back(s[0])
        self._thisptr = new lazy_segtree(sv[0])
    cpdef void set(self, int p, vector[int] v):
        cdef S *s = new S(v.at(0), v.at(1))
        self._thisptr.set(p, s[0])
    cpdef int get(self, int p):
        cdef S *s = new S(self._thisptr.get(p))
        return s.get_a()
    cpdef int prod(self, int l, int r):
        cdef S *s = new S(self._thisptr.prod(l, r))
        return s.get_a()
    cpdef int all_prod(self):
        cdef S *s = new S(self._thisptr.all_prod())
        return s.get_a()
    cpdef void apply(self, int p, int _f):
        cdef F *f = new F(_f)
        self._thisptr.apply(p, f[0])
    cpdef void apply_range(self, int l, int r, int _f):
        cdef F *f = new F(_f)
        self._thisptr.apply(l, r, f[0])
cpdef inline vector[int] ReadInt(int n):
    cdef int b, c
    cdef vector[int] *v = new vector[int]()
    for i in range(n):
        c = 0
        while 1:
            b = getchar() - 48
            if b < 0: break
            c = c * 10 + b
        v.push_back(c)
    return v[0]

cpdef inline vector[string] Read(int n):
    cdef char c
    cdef vector[string] *vs = new vector[string]()
    cdef string *s
    for i in range(n):
        s = new string()
        while 1:
            c = getchar()
            if c<=32: break
            s.push_back(c)
        vs.push_back(s[0])
    return vs[0]

cpdef inline void PrintLongN(vector[long] l):
    cdef int n = l.size()
    for i in range(n): printf("%ld\\n", l[i])

cpdef inline void PrintLong(vector[long] l):
    cdef int n = l.size()
    for i in range(n): printf("%ld ", l[i])
"""


import os, sys, getpass

if sys.argv[-1] == 'ONLINE_JUDGE':
    code = code.replace("USERNAME", getpass.getuser())
    open('intermediate.hpp','w').write(header_code)
    open('atcoder.pyx','w').write(code)
    os.system('cythonize -i -3 -b atcoder.pyx')
    sys.exit(0)


from atcoder import LazySegTree, ReadInt

def main():
    N,Q = ReadInt(2)
    lst = LazySegTree([(1,10) for i in range(N)])
    for i in range(Q):
        l,r,d = ReadInt(3)
        lst.apply_range(l-1,r,d)
        print(lst.all_prod())

if __name__ == "__main__":
    main()

Solution (Python):
import sys
sys.setrecursionlimit(10**7)
input = sys.stdin.buffer.readline
mod = 998244353
INF = float("inf")


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

  
	def gindex(self, l, r):
		L = l + self.N0
		L >>= 1
		R = r + self.N0
		R >>= 1
		if l & 1:
			lc = 0
		else:
			lc = (L & -L).bit_length()
		if r & 1:
			rc = 0
		else:
			rc = (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

	
	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 = (s + self.data[R-1])%mod
			if L & 1:
				s = (s + self.data[L-1])%mod
				L += 1
			L >>= 1
			R >>= 1
		return s

N, Q = map(int, input().split())
lSeg = lazySegTree(N)
lSeg.initialize()

for i in range(Q):
	L, R, D = map(int, input().split())
	L -= 1
	R -= 1
	lSeg.update(L, R, D)
	ans = lSeg.query(0, N-1)
	ans %= mod
	print(ans)

Solution (Python):
class LazySegmentTree:

    __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

        
        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

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

        
        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
        
        
        indices = []
        l0 = (l // (l & -l))     // 2
        r0 = (r // (r & -r) - 1) // 2
        while r0 > l0:
            indices.append(r0)
            r0 //= 2
        while l0 > r0:
            indices.append(l0)
            l0 //= 2
        while l0 and l0 != r0:
            indices.append(r0)
            r0 //= 2
            if l0 == r0:
                break
            indices.append(l0)
            l0 //= 2
        while r0:
            indices.append(r0)
            r0 //= 2

        
        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

        
        while l < r:
            if l % 2:
                self.lazy[l] = self.foo(self.lazy[l], operator)
                l += 1
            if r % 2:
                r -= 1
                self.lazy[r] = self.foo(self.lazy[r], operator)
            l //= 2
            r //= 2

        
        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

        
        indices = []
        l0 = (l // (l & -l))     // 2
        r0 = (r // (r & -r) - 1) // 2
        while r0 > l0:
            indices.append(r0)
            r0 //= 2
        while l0 > r0:
            indices.append(l0)
            l0 //= 2
        while l0 and l0 != r0:
            indices.append(r0)
            r0 //= 2
            if l0 == r0:
                break
            indices.append(l0)
            l0 //= 2
        while r0:
            indices.append(r0)
            r0 //= 2
        
        
        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

        
        left_folded = self.me
        right_folded = self.me
        while l < r:
            if l % 2:
                left_folded = self.fmm(left_folded, self.fmo(self.data[l], self.lazy[l]))
                l += 1
            if r % 2:
                r -= 1
                right_folded = self.fmm(self.fmo(self.data[r], self.lazy[r]), right_folded)
            l //= 2
            r //= 2
        return self.fmm(left_folded, right_folded)


mod = 998244353
N,Q = map(int, input().split())
monoid_data = [(1,1) for _ in range(N)]
ones = [0,1]
pow10 = [1]
for i in range(N):
    ones.append((ones[-1]*10+1) % mod)
    pow10.append(pow10[-1]*10 % mod)

def fmm(m1, m2):
    return ((m1[0]*pow10[m2[1]]+m2[0]) % mod,m1[1]+m2[1])

def fmo(m1, o1):
    if o1 != -1:
        return (ones[m1[1]]*o1 % mod,m1[1])
    else:
        return m1

def foo(o1, o2):
    if o2 == -1:
        return o1
    else:
        return o2

lst = LazySegmentTree(monoid_data, (0,0), -1, fmm, fmo, foo)
for i in range(Q):
    l,r,d = map(int,input().split())
    lst.effect(l-1,r,d)
    print(lst.folded(0,N)[0])

Solution (Python):

import sys

input = sys.stdin.buffer.readline

mod = 998244353


max_n = 262144 + 10
pow_list = [1] * max_n
cum_pow = [0] * max_n
cum_pow[0] = 1
for i in range(1, max_n):
    pow_list[i] = pow_list[i - 1] * 10
    pow_list[i] %= mod
    cum_pow[i] += cum_pow[i - 1] + pow_list[i]
    cum_pow[i] %= mod
cum_pow = [0] + cum_pow


class LazySegmentTree:
    
    def __init__(self, n, ele, lazy_ele, mod=mod):
        self.n = n
        self.ele = ele
        self.lazy_ele = lazy_ele
        self.num = 2 ** ((self.n - 1).bit_length())
        self.SEG = [self.ele] * (2 * self.num)
        self.LAZY = [self.lazy_ele] * (2 * self.num)
        self.node = [1] * (self.num + self.n) + [0] * (self.num - self.n)
        for i in reversed(range(1, self.num)):
            self.node[i] = self.node[2 * i] + self.node[2 * i + 1]

    
    def func(self, a, b, b_node):
        if b == 0:
            return a
        if a == 0:
            return b
        return (a * pow_list[b_node] + b) % mod

    
    def calc(self, idx):
        if self.LAZY[idx] == -1:
            return self.SEG[idx]
        node_num = self.node[idx]
        res = (self.LAZY[idx] * cum_pow[node_num]) % mod
        return res

    
    def lazy_func(self, a, b):
        return b

    
    def initialize(self, A):
        for i, val in enumerate(A):
            i += self.num
            self.SEG[i] = val
        for i in reversed(range(1, self.num)):
            self.SEG[i] = self.func(
                self.SEG[2 * i], self.SEG[2 * i + 1], self.node[2 * i + 1]
            )

    
    def propagate_point(self, idx):
        if self.LAZY[idx] >= 0:
            self.SEG[idx] = self.calc(idx)
            
            self.LAZY[2 * idx] = self.lazy_func(self.LAZY[2 * idx], self.LAZY[idx])
            self.LAZY[2 * idx + 1] = self.lazy_func(
                self.LAZY[2 * idx + 1], self.LAZY[idx]
            )
            self.LAZY[idx] = lazy_ele

    
    def propagate_above(self, idx):
        height = idx.bit_length() - 1
        
        for h in reversed(range(1, height + 1)):
            self.propagate_point(idx >> h)

    
    def recalc_above(self, idx):
        
        while idx > 1:
            idx //= 2
            self.SEG[idx] = self.func(
                self.calc(2 * idx), self.calc(2 * idx + 1), self.node[2 * idx + 1],
            )

    
    def update_point(self, idx):
        idx += self.num
        while idx > 0:
            idx //= 2
            self.SEG[idx] = self.func(
                self.calc(self.SEG[2 * idx]),
                self.calc(self.SEG[2 * idx + 1]),
                self.node[2 * idx + 1],
            )

    
    def update_range(self, left, right, mono):
        left += self.num
        right += self.num
        left_temp = left // (left & -left)
        right_temp = right // (right & -right) - 1
        self.propagate_above(left_temp)
        self.propagate_above(right_temp)
        while left < right:
            if left & 1:
                self.LAZY[left] = self.lazy_func(self.LAZY[left], mono)
                left += 1
            if right & 1:
                right -= 1
                self.LAZY[right] = self.lazy_func(self.LAZY[right], mono)
            left //= 2
            right //= 2
        self.recalc_above(left_temp)
        self.recalc_above(right_temp)

    
    def get_range(self, left, right):
        left += self.num
        right += self.num
        self.propagate_above(left // (left & -left))
        self.propagate_above(right // (right & -right) - 1)
        res_left = self.ele
        res_left_node = 0
        res_right = self.ele
        res_right_node = 0
        while left < right:
            if left & 1:
                res_left = self.func(res_left, self.calc(left), res_left_node)
                res_left_node += self.node[left]
                left += 1
            if right & 1:
                right -= 1
                res_right = self.func(self.calc(right), res_right, res_right_node)
                res_right_node += self.node[right]
            left //= 2
            right //= 2
        
        return self.func(res_right, res_left, res_left_node) % mod

    
    
    


ele = 0
lazy_ele = -1
n, q = map(int, input().split())
A = [1 for _ in range(n)]



LST = LazySegmentTree(n, ele, lazy_ele)
LST.initialize(A)


for _ in range(q):
    l, r, d = map(int, input().split())
    l -= 1

    LST.update_range(l, r, d)

    print(LST.get_range(0, n) % mod)