Buggy python submission (verdict: Time Limit Exceeded):
# 借りた。感謝。
import sys
MOD = 998244353
mask32 = (1 << 32) - 1


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)

    # 1点更新
    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)

    # 1点取得
    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]

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

    # L固定時の最長区間のR
    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

    # R固定時の最長区間のL
    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


def e():
    return 0


def fmm(m1, m2):
    v1 = m1 >> 32
    l1 = m1 & mask32
    v2 = m2 >> 32
    l2 = m2 & mask32
    v = (v1 * pow(10, l2, MOD) + v2) % MOD
    l = l1 + l2
    return (v << 32) + l


inv9 = pow(9, MOD - 2, MOD)


def fmo(o, m):
    if o == -1:
        return m
    else:
        l = m & mask32
        v = o * (pow(10, l, MOD) - 1) * inv9 % MOD
        return (v << 32) + l


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


def id():
    return -1


input = sys.stdin.buffer.readline
read = sys.stdin.buffer.read


N, Q = map(int, input().split())

lst = lazy_segtree(fmm, e, fmo, foo, id, [(1 << 32) + 1] * N)

LRDs = map(int, read().split())
for L, R, D in zip(LRDs, LRDs, LRDs):
    lst.apply(L - 1, R, D)
    print(lst.all_prod() >> 32)


Fixed submission:
# AtCoderのPyPy3環境だとこれが一番速い。


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

        # 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
        l0 = l
        r0 = r - 1
        while l0 % 2 == 0:
            l0 //= 2
        while r0 % 2 == 1:
            r0 //= 2

        # propagation
        for shift in range(l0.bit_length()-1, 0, -1):
            i = l0 >> 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
        for shift in range(r0.bit_length()-1, 0, -1):
            i = r0 >> 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

        # effect
        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

        # recalculation
        i = l0
        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
        i = r0
        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 all_folded(self):
        return self.data[1]


    def folded(self, l, r):
        l += self.n
        r += self.n
        l0 = l
        r0 = r - 1
        while l0 % 2 == 0:
            l0 //= 2
        while r0 % 2 == 1:
            r0 //= 2

        # propagation
        for shift in range(l0.bit_length()-1, 0, -1):
            i = l0 >> 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
        for shift in range(r0.bit_length()-1, 0, -1):
            i = r0 >> 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

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


import sys
input = sys.stdin.buffer.readline
read = sys.stdin.buffer.read

MOD = 998244353
mask32 = (1 << 32) - 1


N, Q = map(int, input().split())


pw = [1]
for _ in range(N + 2):
    pw.append(pw[-1] * 10 % MOD)


# 値 << 32 + 長さ
def fmm(m1, m2):
    v1 = m1 >> 32
    l1 = m1 & mask32
    v2 = m2 >> 32
    l2 = m2 & mask32
    v = (v1 * pw[l2] + v2) % MOD
    l = l1 + l2
    return (v << 32) + l

inv9 = pow(9, MOD - 2, MOD)
def fmo(m, o):
    if o == -1:
        return m
    else:
        l = m & mask32
        v = o * (pw[l] - 1) * inv9 % MOD
        return (v << 32) + l

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

lst = LazySegmentTree([(1 << 32) + 1] * N, 0, -1, fmm, fmo, foo)

LRDs = map(int, read().split())
for L, R, D in zip(LRDs, LRDs, LRDs):
    lst.effect(L - 1, R, D)
    print(lst.folded(0, N) >> 32)

Buggy python submission (verdict: Time Limit Exceeded):
# AtCoderのPyPy3環境だとこれが一番速い。

MOD = 998244353
mask32 = (1 << 32) - 1

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

        # 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
        l0 = l
        r0 = r - 1
        while l0 % 2 == 0:
            l0 //= 2
        while r0 % 2 == 1:
            r0 //= 2

        # propagation
        for shift in range(l0.bit_length()-1, 0, -1):
            i = l0 >> 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
        for shift in range(r0.bit_length()-1, 0, -1):
            i = r0 >> 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

        # effect
        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

        # recalculation
        i = l0
        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
        i = r0
        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 folded(self, l, r):
        l += self.n
        r += self.n
        l0 = l
        r0 = r - 1
        while l0 % 2 == 0:
            l0 //= 2
        while r0 % 2 == 1:
            r0 //= 2

        # propagation
        for shift in range(l0.bit_length()-1, 0, -1):
            i = l0 >> 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
        for shift in range(r0.bit_length()-1, 0, -1):
            i = r0 >> 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

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


import sys
input = sys.stdin.buffer.readline
read = sys.stdin.buffer.read

N, Q = map(int, input().split())

# 値 << 32 + 長さ
def fmm(m1, m2):
    v1 = m1 >> 32
    l1 = m1 & mask32
    v2 = m2 >> 32
    l2 = m2 & mask32
    v = (v1 * pow(10, l2, MOD) + v2) % MOD
    l = l1 + l2
    return (v << 32) + l

inv9 = pow(9, MOD - 2, MOD)
def fmo(m, o):
    if o == -1:
        return m
    else:
        l = m & mask32
        v = o * (pow(10, l, MOD) - 1) * inv9 % MOD
        return (v << 32) + l

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

lst = LazySegmentTree([(1 << 32) + 1] * N, 0, -1, fmm, fmo, foo)

LRDs = map(int, read().split())
for L, R, D in zip(LRDs, LRDs, LRDs):
    lst.effect(L - 1, R, D)
    print(lst.folded(0, N) >> 32)



Fixed submission:
# AtCoderのPyPy3環境だとこれが一番速い。


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

        # 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
        l0 = l
        r0 = r - 1
        while l0 % 2 == 0:
            l0 //= 2
        while r0 % 2 == 1:
            r0 //= 2

        # propagation
        for shift in range(l0.bit_length()-1, 0, -1):
            i = l0 >> 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
        for shift in range(r0.bit_length()-1, 0, -1):
            i = r0 >> 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

        # effect
        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

        # recalculation
        i = l0
        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
        i = r0
        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 all_folded(self):
        return self.data[1]


    def folded(self, l, r):
        l += self.n
        r += self.n
        l0 = l
        r0 = r - 1
        while l0 % 2 == 0:
            l0 //= 2
        while r0 % 2 == 1:
            r0 //= 2

        # propagation
        for shift in range(l0.bit_length()-1, 0, -1):
            i = l0 >> 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
        for shift in range(r0.bit_length()-1, 0, -1):
            i = r0 >> 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

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


import sys
input = sys.stdin.buffer.readline
read = sys.stdin.buffer.read

MOD = 998244353
mask32 = (1 << 32) - 1


N, Q = map(int, input().split())


pw = [1]
for _ in range(N + 2):
    pw.append(pw[-1] * 10 % MOD)


# 値 << 32 + 長さ
def fmm(m1, m2):
    v1 = m1 >> 32
    l1 = m1 & mask32
    v2 = m2 >> 32
    l2 = m2 & mask32
    v = (v1 * pw[l2] + v2) % MOD
    l = l1 + l2
    return (v << 32) + l

inv9 = pow(9, MOD - 2, MOD)
def fmo(m, o):
    if o == -1:
        return m
    else:
        l = m & mask32
        v = o * (pw[l] - 1) * inv9 % MOD
        return (v << 32) + l

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

lst = LazySegmentTree([(1 << 32) + 1] * N, 0, -1, fmm, fmo, foo)

LRDs = map(int, read().split())
for L, R, D in zip(LRDs, LRDs, LRDs):
    lst.effect(L - 1, R, D)
    print(lst.folded(0, N) >> 32)