package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

var sc, wr = bufio.NewScanner(os.Stdin), bufio.NewWriter(os.Stdout)

func scanString() string   { sc.Scan(); return sc.Text() }
func scanRunes() []rune    { return []rune(scanString()) }
func scanInt() int         { a, _ := strconv.Atoi(scanString()); return a }
func scanInt64() int64     { a, _ := strconv.ParseInt(scanString(), 10, 64); return a }
func scanFloat64() float64 { a, _ := strconv.ParseFloat(scanString(), 64); return a }

func scanInts(n int) []int {
	a := make([]int, n)
	for i := 0; i < n; i++ {
		a[i] = scanInt()
	}
	return a
}

func debug(a ...interface{}) {
	if os.Getenv("ONLINE_JUDGE") == "false" {
		fmt.Fprintln(os.Stderr, a...)
	}
}

func abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

//•*¨*•.¸¸♪main•*¨*•.¸¸♪(　-ω-)ノ　(　・ω・)
func main() {
	defer wr.Flush()
	sc.Split(bufio.ScanWords)
	sc.Buffer(make([]byte, 1001), 1001001)

	n, q := scanInt(), scanInt()
	lsg := newLazySegmentTree(n, node{0, 0}, 0, f, g, h)

	mul = make([]int, n+1)
	mul[0] = 1
	for i := 0; i < n; i++ {
		mul[i+1] = (mul[i]*10%mod + 1) % mod
	}

	for i := 0; i < n; i++ {
		lsg.set(i, node{1, 1})
	}
	lsg.build()

	for i := 0; i < q; i++ {
		l, r, d := scanInt()-1, scanInt(), scanInt()
		lsg.update(l, r, nodeL(d))
		fmt.Fprintln(wr, lsg.query(0, n).v)
	}
}

var mul []int

const mod = 998244353

func mpow(a, n int) int {
	r := 1
	for n > 0 {
		if n&1 == 1 {
			r = r * a % mod
		}
		a = a * a % mod
		n >>= 1
	}
	return r
}

func f(a, b node) node {
	if b.v == 0 {
		return a
	}
	return node{(a.v*mpow(10, b.h)%mod + b.v) % mod, a.h + b.h}
}

func g(a node, b nodeL) node {
	if b == 0 {
		return a
	}
	return node{mul[a.h-1] * int(b), a.h}
}

func h(a, b nodeL) nodeL {
	if a == 0 {
		return b
	}
	return a
}

type node struct {
	v int
	h int
}
type nodeL int

type op func(a, b node) node
type mp func(a node, b nodeL) node
type cp func(a, b nodeL) nodeL
type lazySegmentTree struct {
	d  []node
	lz []nodeL
	n  int
	sz int
	lg int
	e  node
	el nodeL
	f  op
	g  mp
	h  cp
}

func newLazySegmentTree(n int, e node, el nodeL, f op, g mp, h cp) *lazySegmentTree {
	sz, lg := 1, 0
	for sz < n {
		sz <<= 1
		lg++
	}
	d := make([]node, 2*sz)
	lz := make([]nodeL, sz)
	for i := 0; i < 2*sz; i++ {
		d[i] = e
	}
	for i := 0; i < sz; i++ {
		lz[i] = el
	}
	s := lazySegmentTree{d, lz, n, sz, lg, e, el, f, g, h}
	return &s
}

func (s *lazySegmentTree) set(i int, a node) {
	s.d[i+s.sz] = a
}

func (s *lazySegmentTree) build() {
	for i := s.sz - 1; i > 0; i-- {
		s.d[i] = s.f(s.d[2*i], s.d[2*i+1])
	}
}

func (s *lazySegmentTree) get(k int) node {
	k += s.sz
	for i := s.lg; i > 0; i-- {
		s.prop(k >> i)
	}
	return s.d[k]
}

func (s *lazySegmentTree) update(l, r int, x nodeL) {
	l += s.sz
	r += s.sz
	for i := s.lg; i > 0; i-- {
		if l>>i<<i != l {
			s.prop(l >> i)
		}
		if r>>i<<i != r {
			s.prop((r - 1) >> i)
		}
	}

	for ll, rr := l, r; ll < rr; ll, rr = ll>>1, rr>>1 {
		if ll&1 == 1 {
			s.apply(ll, x)
			ll++
		}
		if rr&1 == 1 {
			rr--
			s.apply(rr, x)
		}
	}

	for i := 1; i <= s.lg; i++ {
		if l>>i<<i != l {
			k := l >> i
			s.d[k] = s.f(s.d[2*k], s.d[2*k+1])
		}
		if r>>i<<i != r {
			k := (r - 1) >> i
			s.d[k] = s.f(s.d[2*k], s.d[2*k+1])
		}
	}
}

func (s *lazySegmentTree) query(l, r int) node {
	vl, vr := s.e, s.e
	l += s.sz
	r += s.sz

	for i := s.lg; i > 0; i-- {
		if l>>i<<i != l {
			s.prop(l >> i)
		}
		if r>>i<<i != r {
			s.prop(r >> i)
		}
	}

	for ; l < r; l, r = l>>1, r>>1 {
		if l&1 == 1 {
			vl = s.f(vl, s.d[l])
			l++
		}
		if r&1 == 1 {
			r--
			vr = s.f(s.d[r], vr)
		}
	}
	return s.f(vl, vr)
}

func (s *lazySegmentTree) maxRight(l int, f func(v node) bool) int {
	if l == s.n {
		return s.n
	}
	v := s.e
	l += s.sz
	for i := s.lg; i > 0; i-- {
		s.prop(l >> i)
	}

	for {
		for l&1 == 0 {
			l >>= 1
		}
		if !f(s.f(v, s.d[l])) {
			for l < s.sz {
				l <<= 1
				if f(s.f(v, s.d[l])) {
					v = s.f(v, s.d[l])
					l++
				}
			}
			return l - s.sz
		}
		v = s.f(v, s.d[l])
		l++
		if l&-l == l {
			break
		}
	}

	return s.n
}

func (s *lazySegmentTree) minLeft(r int, f func(v node) bool) int {
	if r == 0 {
		return 0
	}
	v := s.e
	r += s.sz
	for i := s.lg; i > 0; i-- {
		s.prop((r - 1) >> i)
	}

	for {
		r--
		for r > 1 && r&1 == 1 {
			r >>= 1
		}
		if !f(s.f(v, s.d[r])) {
			for r < s.sz {
				r = r<<1 + 1
				if f(s.f(v, s.d[r])) {
					v = s.f(v, s.d[r])
					r--
				}
			}
			return r + 1 - s.sz
		}
		v = s.f(v, s.d[r])
		if r&-r == r {
			break
		}
	}

	return 0
}

func (s *lazySegmentTree) prop(k int) {
	s.apply(2*k, s.lz[k])
	s.apply(2*k+1, s.lz[k])
	s.lz[k] = s.el
}

func (s *lazySegmentTree) apply(k int, x nodeL) {
	s.d[k] = s.g(s.d[k], x)
	if k < s.sz {
		s.lz[k] = s.h(x, s.lz[k])
	}
}


Measured memory_bytes: 18572