#[allow(unused_imports)]
use {
    itertools::Itertools,
    proconio::{fastout, input, marker::*},
    std::cmp::*,
    std::collections::*,
    std::io::Write,
    std::ops::*,
};

#[allow(unused_macros)]
macro_rules! debug { ($($e:expr),*) => { #[cfg(debug_assertions)] $({ let (e, mut err) = (stringify!($e), std::io::stderr()); writeln!(err, "{} = {:?}", e, $e).unwrap() })* }; }

#[derive(Copy, Clone, PartialEq)]
pub struct Mint(i64);
impl Mint {
    fn new(x: i64) -> Self {
        Mint(x.rem_euclid(MOD))
    }
    fn pow(self, n: usize) -> Self {
        match n {
            0 => Mint::new(1),
            _ => {
                let mut a = self.pow(n >> 1);
                a *= a;
                if n & 1 == 1 {
                    a *= self;
                }
                a
            }
        }
    }
    fn inv(self) -> Self {
        self.pow((MOD - 2) as usize)
    }
}
impl std::ops::Neg for Mint {
    type Output = Mint;
    fn neg(self) -> Self::Output {
        Self::new(-self.0)
    }
}
impl std::ops::AddAssign<Mint> for Mint {
    fn add_assign(&mut self, rhs: Self) {
        self.0 += rhs.0;
        self.0 %= MOD;
    }
}
impl std::ops::AddAssign<i64> for Mint {
    fn add_assign(&mut self, rhs: i64) {
        *self += Mint::new(rhs);
    }
}
impl<T> std::ops::Add<T> for Mint
where
    Mint: std::ops::AddAssign<T>,
{
    type Output = Self;
    fn add(self, other: T) -> Self {
        let mut res = self;
        res += other;
        res
    }
}
impl std::ops::SubAssign<Mint> for Mint {
    fn sub_assign(&mut self, rhs: Self) {
        self.0 -= rhs.0;
        if self.0 < 0 {
            self.0 += MOD;
        }
    }
}
impl std::ops::SubAssign<i64> for Mint {
    fn sub_assign(&mut self, rhs: i64) {
        *self -= Mint::new(rhs);
    }
}
impl<T> std::ops::Sub<T> for Mint
where
    Mint: std::ops::SubAssign<T>,
{
    type Output = Self;
    fn sub(self, other: T) -> Self {
        let mut res = self;
        res -= other;
        res
    }
}
impl std::ops::MulAssign<Mint> for Mint {
    fn mul_assign(&mut self, rhs: Self) {
        self.0 *= rhs.0;
        self.0 %= MOD;
    }
}
impl std::ops::MulAssign<i64> for Mint {
    fn mul_assign(&mut self, rhs: i64) {
        *self *= Mint::new(rhs);
    }
}
impl<T> std::ops::Mul<T> for Mint
where
    Mint: std::ops::MulAssign<T>,
{
    type Output = Self;
    fn mul(self, other: T) -> Self {
        let mut res = self;
        res *= other;
        res
    }
}
impl std::ops::DivAssign<Mint> for Mint {
    fn div_assign(&mut self, rhs: Self) {
        *self *= rhs.inv();
    }
}
impl std::ops::DivAssign<i64> for Mint {
    fn div_assign(&mut self, rhs: i64) {
        *self /= Mint::new(rhs);
    }
}
impl<T> std::ops::Div<T> for Mint
where
    Mint: std::ops::DivAssign<T>,
{
    type Output = Self;
    fn div(self, other: T) -> Self {
        let mut res = self;
        res /= other;
        res
    }
}
impl std::fmt::Display for Mint {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}
impl std::ops::Deref for Mint {
    type Target = i64;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
impl std::ops::DerefMut for Mint {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

trait ME {
    type M: Clone;
    type E: Clone + PartialEq;
    fn id_m() -> Self::M;
    fn id_e() -> Self::E;
    fn id() -> (Self::M, Self::E) {
        (Self::id_m(), Self::id_e())
    }
    fn f(a: &Self::M, b: &Self::M) -> Self::M;
    fn g(a: &Self::M, b: &Self::E) -> Self::M;
    fn h(a: &Self::E, b: &Self::E) -> Self::E;
    fn p(a: &Self::E, k: usize) -> Self::E;
}
struct LazySegmentTree<T: ME> {
    width: usize,
    dat: Vec<(T::M, T::E)>,
}
#[allow(dead_code)]
impl<T: ME> LazySegmentTree<T> {
    fn new(n: usize) -> Self {
        let width = n.next_power_of_two();
        Self {
            width,
            dat: vec![T::id(); (width << 1) - 1],
        }
    }
    fn from_vec(a: &[T::M]) -> Self {
        let width = a.len().next_power_of_two();
        let mut dat = vec![T::id(); (width << 1) - 1];
        for i in 0..a.len() {
            dat[i + width - 1].0 = a[i].clone();
        }
        for i in (0..width - 1).rev() {
            dat[i].0 = T::f(&dat[(i << 1) + 1].0, &dat[(i << 1) + 2].0);
        }
        Self { width, dat }
    }
    fn __eval(&mut self, now: usize, k: usize) {
        if self.dat[now].1 != T::id_e() {
            let e = T::p(&self.dat[now].1, k);
            self.dat[now].0 = T::g(&self.dat[now].0, &e);
            if k > 1 {
                self.dat[(now << 1) + 1].1 = T::h(&self.dat[(now << 1) + 1].1, &self.dat[now].1);
                self.dat[(now << 1) + 2].1 = T::h(&self.dat[(now << 1) + 2].1, &self.dat[now].1);
            }
            self.dat[now].1 = T::id_e();
        }
    }
    fn __update(&mut self, x: &T::E, now: usize, lc: usize, rc: usize, l: usize, r: usize) {
        self.__eval(now, rc - lc);
        if l <= lc && rc <= r {
            self.dat[now].1 = T::h(&self.dat[now].1, x);
            self.__eval(now, rc - lc);
        } else if l < rc && lc < r {
            self.__update(x, (now << 1) + 1, lc, (lc + rc) / 2, l, r);
            self.__update(x, (now << 1) + 2, (lc + rc) / 2, rc, l, r);
            self.dat[now].0 = T::f(&self.dat[(now << 1) + 1].0, &self.dat[(now << 1) + 2].0);
        }
    }
    fn update<R>(&mut self, range: R, x: &T::E)
    where
        R: std::ops::RangeBounds<usize>,
    {
        let l = match range.start_bound() {
            std::ops::Bound::Included(&a) => a,
            _ => 0,
        };
        let r = match range.end_bound() {
            std::ops::Bound::Excluded(&a) => a,
            std::ops::Bound::Included(&a) => a + 1,
            _ => self.width,
        };
        self.__update(x, 0, 0, self.width, l, r);
    }
    fn __prod(&mut self, now: usize, lc: usize, rc: usize, l: usize, r: usize) -> T::M {
        self.__eval(now, rc - lc);
        if rc <= l || r <= lc {
            T::id_m()
        } else if l <= lc && rc <= r {
            self.dat[now].0.clone()
        } else {
            T::f(
                &self.__prod((now << 1) + 1, lc, (lc + rc) >> 1, l, r),
                &self.__prod((now << 1) + 2, (lc + rc) >> 1, rc, l, r),
            )
        }
    }
    fn prod<R>(&mut self, range: R) -> T::M
    where
        R: std::ops::RangeBounds<usize>,
    {
        let l = match range.start_bound() {
            std::ops::Bound::Included(&a) => a,
            _ => 0,
        };
        let r = match range.end_bound() {
            std::ops::Bound::Excluded(&a) => a,
            std::ops::Bound::Included(&a) => a + 1,
            _ => self.width,
        };
        self.__prod(0, 0, self.width, l, r)
    }
}

struct Node;
impl ME for Node {
    type M = (Mint, usize);
    type E = Mint;
    fn id_m() -> Self::M {
        (Mint(0), 0)
    }
    fn id_e() -> Self::E {
        Mint(0)
    }
    fn f((a, k): &Self::M, (b, l): &Self::M) -> Self::M {
        let a = *a * Mint(10).pow(*l);
        (a + *b, k + l)
    }
    fn g((_, k): &Self::M, b: &Self::E) -> Self::M {
        (*b, *k)
    }
    fn h(_a: &Self::E, b: &Self::E) -> Self::E {
        *b
    }
    fn p(a: &Self::E, k: usize) -> Self::E {
        unsafe { *a * TEN[k] }
    }
}
const MOD: i64 = 998244353;
static mut TEN: Vec<Mint> = Vec::new();

#[fastout]
fn main() {
    input! {
        n: usize,
        q: usize,
        lrd: [(Usize1, Usize1, i64); q],
    }
    unsafe {
        TEN.push(Mint(0));
        let mut x = Mint(1);
        for _ in 0..n {
            TEN.push(x);
            x *= 10;
            x += 1;
        }
    }
    let v: Vec<_> = (0..n).map(|_| (Mint(1), 1)).collect();
    let mut seg = LazySegmentTree::<Node>::from_vec(&v);

    for (l, r, d) in lrd {
        seg.update(l..=r, &Mint(d));
        let ans = seg.prod(..n).0;
        println!("{}", ans);
    }
}


Measured memory_bytes: 27068