use atcoder_lazysegtree::*;
use input_mcr::*;
use std::cmp::*;

use modular::*;
type Mod = Modular<Mod_998_244_353>;

#[derive(Debug, Clone)]
struct Context {
    ten: Vec<Mod>,
    one: Vec<Mod>,
}

#[derive(Debug, Clone, Copy)]
struct S {
    len: usize,
    val: Mod,
}

#[derive(Debug, Clone, Copy)]
struct F(i64);

impl LazySegmentTreeContext for Context {
    type S = S;
    fn op(&self, l: S, r: S) -> S {
        S {
            len: l.len + r.len,
            val: l.val * self.ten[r.len] + r.val,
        }
    }
    fn e(&self) -> S {
        S {
            len: 0,
            val: Mod::new(0),
        }
    }
    type F = F;
    fn mapping(&self, f: F, s: S) -> S {
        if f.0 == 0 {
            s
        } else {
            S {
                len: s.len,
                val: self.one[s.len] * Mod::new(f.0),
            }
        }
    }
    fn composition(&self, l: F, r: F) -> F {
        if l.0 == 0 {
            r
        } else {
            l
        }
    }
    fn id(&self) -> F {
        F(0)
    }
}

fn main() {
    input! {
        n: usize,
        q: usize,
        qs: [(usize1,usize1,i64); q],
    }
    let mut ten = vec![Mod::new(1)];
    for i in 1..200_100 {
        let t = ten[i - 1] * Mod::new(10);
        ten.push(t);
    }
    let mut one = vec![Mod::new(0)];
    for i in 1..200_100 {
        let t = one[i - 1] * Mod::new(10) + Mod::new(1);
        one.push(t);
    }

    let unit = S {
        len: 1,
        val: Mod::new(1),
    };

    let v = vec![unit; n];
    let c = Context { ten, one };
    let mut seg = LazySegmentTree::new_by_vec(c, &v);
    for &(l, r, x) in &qs {
        seg.apply_range(l, r + 1, F(x));
        let res = seg.prod(0, n).val;
        println!("{}", res.as_u32());
    }
}
pub mod input_mcr {
    // ref: tanakh <https://qiita.com/tanakh/items/0ba42c7ca36cd29d0ac8>
    #[macro_export(local_inner_macros)]
    macro_rules! input {
        (source = $s:expr, $($r:tt)*) => {
            let mut parser = Parser::from_str($s);
            input_inner!{parser, $($r)*}
        };
        (parser = $parser:ident, $($r:tt)*) => {
            input_inner!{$parser, $($r)*}
        };
        (new_stdin_parser = $parser:ident, $($r:tt)*) => {
            let stdin = std::io::stdin();
            let reader = std::io::BufReader::new(stdin.lock());
            let mut $parser = Parser::new(reader);
            input_inner!{$parser, $($r)*}
        };
        ($($r:tt)*) => {
            input!{new_stdin_parser = parser, $($r)*}
        };
    }

    #[macro_export(local_inner_macros)]
    macro_rules! input_inner {
        ($parser:ident) => {};
        ($parser:ident, ) => {};
        ($parser:ident, $var:ident : $t:tt $($r:tt)*) => {
            let $var = read_value!($parser, $t);
            input_inner!{$parser $($r)*}
        };
    }

    #[macro_export(local_inner_macros)]
    macro_rules! read_value {
        ($parser:ident, ( $($t:tt),* )) => {
            ( $(read_value!($parser, $t)),* )
        };
        ($parser:ident, [ $t:tt ; $len:expr ]) => {
            (0..$len).map(|_| read_value!($parser, $t)).collect::<Vec<_>>()
        };
        ($parser:ident, chars) => {
            read_value!($parser, String).chars().collect::<Vec<char>>()
        };
        ($parser:ident, char_) => {
            read_value!($parser, String).chars().collect::<Vec<char>>()[0]
        };
        ($parser:ident, usize1) => {
            read_value!($parser, usize) - 1
        };
        ($parser:ident, line) => {
            $parser.next_line()
        };
        ($parser:ident, line_) => {
            $parser.next_line().chars().collect::<Vec<char>>()
        };
        ($parser:ident, $t:ty) => {
            $parser.next::<$t>().expect("Parse error")
        };
    }

    use std::collections::VecDeque;
    use std::io;
    use std::io::BufRead;
    use std::str;

    pub struct Parser<R> {
        pub reader: R,
        buf: VecDeque<u8>,
        parse_buf: Vec<u8>,
    }

    impl Parser<io::Empty> {
        pub fn from_str(s: &str) -> Parser<io::Empty> {
            Parser {
                reader: io::empty(),
                buf: VecDeque::from(s.as_bytes().to_vec()),
                parse_buf: vec![],
            }
        }
    }

    impl<R: BufRead> Parser<R> {
        pub fn new(reader: R) -> Parser<R> {
            Parser {
                reader: reader,
                buf: VecDeque::new(),
                parse_buf: vec![],
            }
        }
        pub fn update_buf(&mut self) {
            loop {
                let (len, complete) = {
                    let buf2 = self.reader.fill_buf().unwrap();
                    self.buf.extend(buf2.iter());
                    let len = buf2.len();
                    (len, buf2.last() < Some(&0x20))
                };
                self.reader.consume(len);
                if complete {
                    break;
                }
            }
        }
        pub fn next<T: str::FromStr>(&mut self) -> Result<T, T::Err> {
            loop {
                while let Some(c) = self.buf.pop_front() {
                    if c > 0x20 {
                        self.buf.push_front(c);
                        break;
                    }
                }
                self.parse_buf.clear();
                while let Some(c) = self.buf.pop_front() {
                    if c <= 0x20 {
                        self.buf.push_front(c);
                        break;
                    } else {
                        self.parse_buf.push(c);
                    }
                }
                if self.parse_buf.is_empty() {
                    self.update_buf();
                } else {
                    return unsafe { str::from_utf8_unchecked(&self.parse_buf) }.parse::<T>();
                }
            }
        }
        pub fn next_line(&mut self) -> String {
            loop {
                while let Some(c) = self.buf.pop_front() {
                    if c >= 0x20 {
                        self.buf.push_front(c);
                        break;
                    }
                }
                self.parse_buf.clear();
                while let Some(c) = self.buf.pop_front() {
                    if c < 0x20 {
                        self.buf.push_front(c);
                        break;
                    } else {
                        self.parse_buf.push(c);
                    }
                }
                if self.parse_buf.is_empty() {
                    self.update_buf();
                } else {
                    return unsafe { str::from_utf8_unchecked(&self.parse_buf) }.to_string();
                }
            }
        }
    }
}
pub mod atcoder_lazysegtree {
    pub trait LazySegmentTreeContext {
        type S: Clone + Copy;
        fn e(&self) -> Self::S;
        fn op(&self, a: Self::S, b: Self::S) -> Self::S;
        type F: Clone + Copy;
        fn mapping(&self, f: Self::F, x: Self::S) -> Self::S;
        fn composition(&self, f: Self::F, g: Self::F) -> Self::F;
        fn id(&self) -> Self::F;
    }

    pub struct LazySegmentTree<C: LazySegmentTreeContext> {
        c: C,
        n: usize,
        size: usize,
        log: usize,
        d: Vec<C::S>,
        lz: Vec<C::F>,
    }

    impl<C: LazySegmentTreeContext> LazySegmentTree<C> {
        pub fn new(c: C, n: usize) -> LazySegmentTree<C> {
            let t = c.e();
            LazySegmentTree::<C>::new_by_vec(c, &vec![t; n])
        }

        pub fn new_by_vec(c: C, v: &Vec<C::S>) -> LazySegmentTree<C> {
            let n = v.len();
            let size = v.len().next_power_of_two();
            let log = size.trailing_zeros() as usize;
            let mut d = vec![c.e(); 2 * size];
            let lz = vec![c.id(); size];
            for i in 0..n {
                d[size + i] = v[i];
            }
            let mut res = LazySegmentTree::<C> {
                c,
                n: v.len(),
                size,
                log,
                d,
                lz,
            };
            for i in (1..size).rev() {
                res.update(i);
            }
            res
        }

        pub fn set(&mut self, p: usize, x: C::S) {
            assert!(p < self.n);
            let p = p + self.size;
            for i in (1..=self.log).rev() {
                self.push(p >> i);
            }
            self.d[p] = x;
            for i in 1..=self.log {
                self.update(p >> i);
            }
        }

        pub fn get(&mut self, p: usize) -> C::S {
            assert!(p < self.n);
            let p = p + self.size;
            for i in (1..=self.log).rev() {
                self.push(p >> i);
            }
            self.d[p]
        }

        pub fn prod(&mut self, l: usize, r: usize) -> C::S {
            assert!(l <= r);
            assert!(r <= self.n);
            if l == r {
                return self.c.e();
            }

            let mut l = l + self.size;
            let mut r = r + self.size;

            for i in (1..=self.log).rev() {
                if ((l >> i) << i) != l {
                    self.push(l >> i);
                }
                if ((r >> i) << i) != r {
                    self.push(r >> i);
                }
            }

            let mut sml = self.c.e();
            let mut smr = self.c.e();
            while l < r {
                if l & 1 != 0 {
                    sml = self.c.op(sml, self.d[l]);
                    l += 1;
                }
                if r & 1 != 0 {
                    r -= 1;
                    smr = self.c.op(self.d[r], smr);
                }
                l >>= 1;
                r >>= 1;
            }

            self.c.op(sml, smr)
        }

        pub fn all_prod(&self) -> C::S {
            self.d[1]
        }

        pub fn apply(&mut self, p: usize, f: C::F) {
            assert!(p < self.n);
            let p = p + self.size;
            for i in (1..=self.log).rev() {
                self.push(p >> i);
            }
            self.d[p] = self.c.mapping(f, self.d[p]);
            for i in 1..=self.log {
                self.update(p >> i);
            }
        }

        pub fn apply_range(&mut self, l: usize, r: usize, f: C::F) {
            assert!(l <= r);
            assert!(r <= self.n);
            if l == r {
                return;
            }

            let mut l = l + self.size;
            let mut r = r + self.size;

            for i in (1..=self.log).rev() {
                if ((l >> i) << i) != l {
                    self.push(l >> i);
                }
                if ((r >> i) << i) != r {
                    self.push((r - 1) >> i);
                }
            }

            {
                let l2 = l;
                let r2 = r;
                while l < r {
                    if l & 1 != 0 {
                        self.all_apply(l, f);
                        l += 1;
                    }
                    if r & 1 != 0 {
                        r -= 1;
                        self.all_apply(r, f);
                    }
                    l >>= 1;
                    r >>= 1;
                }
                l = l2;
                r = r2;
            }

            for i in 1..=self.log {
                if ((l >> i) << i) != l {
                    self.update(l >> i);
                }
                if ((r >> i) << i) != r {
                    self.update((r - 1) >> i);
                }
            }
        }

        // TODO: implement max_right, min_left

        fn update(&mut self, k: usize) {
            self.d[k] = self.c.op(self.d[2 * k], self.d[2 * k + 1]);
        }

        fn all_apply(&mut self, k: usize, f: C::F) {
            self.d[k] = self.c.mapping(f, self.d[k]);
            if k < self.size {
                self.lz[k] = self.c.composition(f, self.lz[k]);
            }
        }

        fn push(&mut self, k: usize) {
            self.all_apply(2 * k, self.lz[k]);
            self.all_apply(2 * k + 1, self.lz[k]);
            self.lz[k] = self.c.id();
        }
    }
}
pub mod modular {
    use std::marker::PhantomData;
    use std::ops::*;

    pub trait ModP {
        fn as_u64() -> u64;
    }

    #[allow(non_camel_case_types)]
    #[derive(Clone, Copy, Debug)]
    pub struct Mod_1_000_000_007();
    impl ModP for Mod_1_000_000_007 {
        fn as_u64() -> u64 {
            1_000_000_007
        }
    }

    #[allow(non_camel_case_types)]
    #[derive(Clone, Copy, Debug)]
    pub struct Mod_998_244_353();
    impl ModP for Mod_998_244_353 {
        fn as_u64() -> u64 {
            998_244_353
        }
    }

    #[derive(Clone, Copy, PartialEq, Debug)]
    pub struct Modular<P>(u32, PhantomData<P>);

    pub trait ToModular<P> {
        fn to_mod(self) -> Modular<P>;
    }

    impl<P: ModP + Copy> ToModular<P> for usize {
        fn to_mod(self) -> Modular<P> {
            let m = P::as_u64() as usize;
            Modular((self % m) as u32, PhantomData)
        }
    }

    impl<P: ModP + Copy> ToModular<P> for i64 {
        fn to_mod(self) -> Modular<P> {
            let m = P::as_u64() as i64;
            Modular(((self % m + m) % m) as u32, PhantomData)
        }
    }

    impl<P: ModP + Copy> ToModular<P> for i32 {
        fn to_mod(self) -> Modular<P> {
            let m = P::as_u64() as i32;
            Modular(((self % m + m) % m) as u32, PhantomData)
        }
    }

    impl<P: ModP + Copy> ToModular<P> for u64 {
        fn to_mod(self) -> Modular<P> {
            let m = P::as_u64() as u64;
            Modular((self % m) as u32, PhantomData)
        }
    }

    impl<P: ModP + Copy> ToModular<P> for u32 {
        fn to_mod(self) -> Modular<P> {
            let m = P::as_u64() as u32;
            Modular((self % m) as u32, PhantomData)
        }
    }

    impl<P: ModP + Copy> Modular<P> {
        pub fn new<T: ToModular<P>>(x: T) -> Modular<P> {
            x.to_mod()
        }
        pub fn pow(self, n: u64) -> Modular<P> {
            if n == 0 {
                return Modular(1, PhantomData);
            }
            let t = self.pow(n / 2);
            if n % 2 == 0 {
                t * t
            } else {
                t * t * self
            }
        }
        pub fn as_u32(self) -> u32 {
            self.0
        }
    }

    impl<P: ModP + Copy> Add for Modular<P> {
        type Output = Self;
        fn add(self, rhs: Self) -> Self::Output {
            let t = (self.0 + rhs.0) as u64;
            let u = if t < P::as_u64() { t } else { t - P::as_u64() };
            Modular(u as u32, PhantomData)
        }
    }

    impl<P: ModP + Copy> AddAssign for Modular<P> {
        fn add_assign(&mut self, rhs: Self) {
            *self = *self + rhs;
        }
    }

    impl<P: ModP + Copy> Sub for Modular<P> {
        type Output = Self;
        fn sub(self, rhs: Self) -> Self::Output {
            let t = if self.0 >= rhs.0 {
                self.0 as u64 - rhs.0 as u64
            } else {
                self.0 as u64 + P::as_u64() - rhs.0 as u64
            };
            assert!(t < P::as_u64());
            Modular(t as u32, PhantomData)
        }
    }

    impl<P: ModP + Copy> SubAssign for Modular<P> {
        fn sub_assign(&mut self, rhs: Self) {
            *self = *self - rhs;
        }
    }

    impl<P: ModP + Copy> Neg for Modular<P> {
        type Output = Modular<P>;
        fn neg(self) -> Modular<P> {
            Modular((P::as_u64() - self.0 as u64) as u32, PhantomData)
        }
    }

    impl<P: ModP + Copy> Mul for Modular<P> {
        type Output = Self;
        fn mul(self, rhs: Self) -> Self::Output {
            Modular(
                ((self.0 as u64 % P::as_u64()) * (rhs.0 as u64 % P::as_u64()) % P::as_u64()) as u32,
                PhantomData,
            )
        }
    }

    impl<P: ModP + Copy> MulAssign for Modular<P> {
        fn mul_assign(&mut self, rhs: Self) {
            *self = *self * rhs;
        }
    }

    impl<P: ModP + Copy> Div for Modular<P> {
        type Output = Self;
        fn div(self, rhs: Self) -> Self::Output {
            if rhs.0 == 0 {
                loop {}
            }
            assert!(rhs.0 != 0);
            self * rhs.pow(P::as_u64() - 2)
        }
    }

    impl<P: ModP + Copy> DivAssign for Modular<P> {
        fn div_assign(&mut self, rhs: Self) {
            *self = *self / rhs;
        }
    }

    pub fn gen_fact_table<P: ModP + Copy>(n: usize) -> Vec<Modular<P>> {
        let mut res = vec![Modular::<P>::new(1); n + 1];
        for i in 0..n {
            res[i + 1] = res[i] * Modular::<P>::new((i + 1) as u32);
        }
        res
    }

    pub fn gen_inv_table<P: ModP + Copy>(n: usize) -> Vec<Modular<P>> {
        let mut res = vec![Modular::new(0); n + 1];
        res[1] = Modular::new(1);
        for i in 2..n + 1 {
            res[i] =
                -res[P::as_u64() as usize % i] * Modular::<P>::new((P::as_u64() / i as u64) as u32);
        }
        res
    }

    /*
    #[test]
    fn test_modular_1_000_000_007() {
        type Mod = Modular<Mod_1_000_000_007>;
        assert_eq!(Mod::new(2).pow(1_000_000).as_u32(), 235_042_059)
    }

    #[test]
    fn test_modular_998_244_353() {
        type Mod = Modular<Mod_998_244_353>;
        assert_eq!(Mod::new(2).pow(1_000_000).as_u32(), 421_273_117);
    }
    */
}


Measured memory_bytes: 23064