macro_rules! read_line_to_tuple {
    ( $( $t:ty ),* ) => {{
        let mut input = String::new();
        std::io::stdin().read_line(&mut input).unwrap();
        let mut iter = input.split_whitespace();
        ( $( iter.next().unwrap().parse::<$t>().unwrap() ),* )
    }};
}

const MOD: i64 = 998_244_353;

struct LazySegmentTree<T, F> {
    n: usize,
    len: usize,
    height: usize,
    op: fn(&Vec<i64>, T, T) -> T,
    e: fn() -> T,
    mapping: fn(&Vec<i64>, F, T) -> T,
    composition: fn(F, F) -> F,
    id: fn() -> F,
    node: Vec<T>,
    lazy: Vec<F>,
    pow10: Vec<i64>,
    init: Vec<i64>,
}

impl<T: Clone + Copy, F: Clone + Copy> LazySegmentTree<T, F> {
    fn new(
        n: usize,
        op: fn(&Vec<i64>, T, T) -> T,
        e: fn() -> T,
        mapping: fn(&Vec<i64>, F, T) -> T,
        composition: fn(F, F) -> F,
        id: fn() -> F,
        ) -> LazySegmentTree<T, F> {
        let (mut len, mut height) = (1, 1);
        while len < n {
            len *= 2;
            height += 1;
        }
        let node = vec![e(); 2 * len];
        let lazy = vec![id(); 2 * len];
        let mut pow10 = vec![0; n + 1];
        pow10[0] = 1;
        for i in 1..=n {
            pow10[i] = pow10[i - 1] * 10 % MOD;
        }
        let mut init = vec![0; n + 1];
        for i in 1..=n {
            init[i] = (init[i - 1] * 10 + 1) % MOD;
        }
        LazySegmentTree {
            n,
            len,
            height,
            op,
            e,
            mapping,
            composition,
            id,
            node,
            lazy,
            pow10,
            init,
        }
    }

    fn from(
        v: Vec<T>,
        op: fn(&Vec<i64>, T, T) -> T,
        e: fn() -> T,
        mapping: fn(&Vec<i64>, F, T) -> T,
        composition: fn(F, F) -> F,
        id: fn() -> F,
        ) -> LazySegmentTree<T, F> {
        let mut lst = LazySegmentTree::new(v.len(), op, e, mapping, composition, id);
        for i in 0..v.len() { lst.node[i + lst.len] = v[i]; }
        for i in (1..lst.len).rev() {
            lst.update(i);
        }
        lst
    }

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

    fn all_apply(&mut self, k: usize, f: F) {
        self.node[k] = (self.mapping)(&self.init, f, self.node[k]);
        if self.len <= k { return; }
        self.lazy[k] = (self.composition)(f, self.lazy[k]);
    }

    fn push(&mut self, k: usize) {
        self.all_apply(2 * k, self.lazy[k]);
        self.all_apply(2* k + 1, self.lazy[k]);
        self.lazy[k] = (self.id)();
    }

    fn _set(&mut self, mut p: usize, x: T) {
        assert!(p < self.n);
        p += self.len;
        for i in (1..self.height).rev() { self.push(p >> i); }
        self.node[p] = x;
        for i in 1..self.height { self.update(p >> i); }
    }

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

        l += self.len;
        r += self.len;

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

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

        (self.op)(&self.pow10, sml, smr)
    }

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

        l += self.len;
        r += self.len;

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

        {
            let (l2, r2) = (l, 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.height {
            if ((l >> i) << i) != l { self.update(l >> i); }
            if ((r >> i) << i) != r { self.update((r - 1) >> i); }
        }
    }
}

#[derive(Clone, Copy)]
struct T { len: usize, val: i64 }

#[derive(Clone, Copy)]
struct F { rewrite: i64 }

fn op(pow10: &Vec<i64>, v1: T, v2: T) -> T {
    let len = v1.len + v2.len;
    let val = (v1.val * pow10[v2.len] + v2.val) % MOD;
    T { len, val }
}

fn e() -> T {
    T { len: 0, val: 0 }
}

fn mapping(init: &Vec<i64>, f: F, v: T) -> T {
    if f.rewrite == 0 { return v; }
    let len = v.len;
    let val = init[v.len] * f.rewrite % MOD;
    T { len, val }
}

fn composition(f1: F, f2: F) -> F {
    if f1.rewrite == 0 {
        f2
    } else {
        f1
    }
}

fn id() -> F {
    F { rewrite: 0 }
}

fn main() {
    let (n, q) = read_line_to_tuple!(usize, usize);

    let v = vec![T { len: 1, val: 1 }; n];
    let mut lst = LazySegmentTree::from(v, op, e, mapping, composition, id);
    for _ in 0..q {
        let (l, r, d) = {
            let (l, r, d) = read_line_to_tuple!(usize, usize, i64);
            (l - 1, r, d)
        };

        lst.apply(l, r, F { rewrite: d });
        println!("{}", lst.prod(0, n).val);
    }
}

Measured memory_bytes: 20540