#pragma GCC target ("avx2")
#pragma GCC optimize ("unroll-loops")
#pragma GCC optimize ("O3")
#include "bits/stdc++.h"
#include <unordered_set>
#include <unordered_map>
#include <random>
using namespace std;
typedef long long ll;
const ll MOD = /*1'000'000'007LL;*/ 998'244'353LL;
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(), (x).end()
#define rep(i, n) for(int (i)=0; (i)<(n); (i)++)
const int dx[4]={ 1,0,-1,0 };
const int dy[4]={ 0,1,0,-1 };

template<long long Mod>
struct mint{
    long long x;
    mint(long long x=0) :x((x% Mod+Mod)%Mod){}

    mint operator-() const { return mint(-x); }

    mint& operator+=(const mint a) {
        if((x += a.x) >= Mod) x -= Mod;
        return *this;
    }
    mint& operator-=(const mint a) {
        if((x += Mod-a.x) >= Mod) x -= Mod;
        return *this;
    }
    mint& operator*=(const mint a) {
        (x *= a.x) %= Mod;
        return *this;
    }

    mint operator^(long long t) const {
        mint ret(1);
        mint a = *this;
        while(t){
            if(t&1){
                ret *= a;
            }
            a *= a;
            t >>= 1;
        }
        return ret;
    }
    mint inv() const { return *this ^ (Mod-2); }
    mint& operator/=(const mint a) { return *this *= a.inv(); }

    mint operator+(const mint a) const { return mint(*this) += a; }
    mint operator-(const mint a) const { return mint(*this) -= a; }
    mint operator*(const mint a) const { return mint(*this) *= a; }
    mint operator/(const mint a) const { return mint(*this) /= a; }

    friend istream& operator>>(istream& is, mint& a) { return is >> a.x; }
    friend ostream& operator<<(ostream& os, const mint& a) { return os << a.x; }
};

template<typename T, typename U>
struct LazySegmentTree{

    int n, N;
    vector<T> node;
    vector<U> lazy;
    function<T(T, T)> F;
    T E;
    function<T(T, U)> G;
    function<U(U, U)> H;
    U I;
public:
    void init(int _n, function<T(T, T)> f, T e, function<T(T, U)> g, function<U(U, U)> h, U i){
        F = f; E = e;
        G = g; H = h; I = i;
        n = _n; N = 1;
        while(N < n) N = (N<<1);
        node.assign(2*N-1, E);
        lazy.assign(2*N-1, I);
    }
    void init(vector<T> v, function<T(T, T)> f, T e, function<T(T, U)> g, function<U(U, U)> h, U i){
        F = f; E = e;
        G = g; H = h; I = i;
        n = v.size(); N = 1;
        while(N < n) N = (N<<1);
        node.assign(2*N-1, E);
        lazy.assign(2*N-1, I);
        for(int i=0; i<n; i++) node[N-1+i] = v[i];
        for(int i=N-2; i>=0; i--) node[i] = F(node[(i<<1)+1], node[(i<<1)+2]);
    }
    int length(){
        return N;
    }
    T operator [](int a){
        return query(a, a+1);
    }
    void eval(int k){
        node[k] = G(node[k], lazy[k]);
        if(k < N-1){
            lazy[(k<<1)+1] = H(lazy[(k<<1)+1], lazy[k]);
            lazy[(k<<1)+2] = H(lazy[(k<<1)+2], lazy[k]);
        }
        lazy[k] = I;
    }
    void apply(int a, int b, U x, int k=0, int l=0, int r=-1){
        if(r == -1) r = N;
        eval(k);
        if(b <= l || r <= a) return;
        if(a <= l && r <= b){
            lazy[k] = H(lazy[k], x);
            eval(k);
        }
        else{
            apply(a, b, H(I, x), (k<<1)+1, l, (l+r)>>1);
            apply(a, b, H(I, x), (k<<1)+2, (l+r)>>1, r);
            node[k] = F(node[(k<<1)+1], node[(k<<1)+2]);
        }
    }
    T query(int a, int b, int k=0, int l=0, int r=-1){
        if(r == -1) r = N;
        if(b <= l || r <= a) return E;
        eval(k);
        if(a <= l && r <= b) return node[k];
        return F(query(a, b, (k<<1)+1, l, (l+r)>>1), query(a, b, (k<<1)+2, (l+r)>>1, r));
    }
    int find_right(function<bool(T)> g, int a){
        if(!g(E)) return -1;
        T t = E;
        return min(find_right(g, a, 0, 0, N, t), n);
    }
    int find_right(function<bool(T)> g, int a, int k, int l, int r, T& t){
        eval(k);
        if(r-l == 1){
            t = F(t, node[k]);
            return g(t) ? r : a;
        }
        int m = (l + r) >> 1;
        if(m <= a) return find_right(g, a, (k<<1)+2, m, r, t);
        if(a <= l && g(F(t, node[k]))){
            t = F(t, node[k]);
            return r;
        }
        int L = find_right(g, a, (k<<1)+1, l, m, t);
        if(L < m) return L;
        int R = find_right(g, a, (k<<1)+2, m, r, t);
        return max(L, R);
    }
    int find_left(function<bool(T)> g, int b){
        if(!g(E)) return n + 1;
        T t = E;
        return find_left(g, b, 0, 0, N, t);
    }
    int find_left(function<bool(T)> g, int b, int k, int l, int r, T t){
        eval(k);
        if(r-l == 1){
            t = F(node[k], t);
            return g(t) ? l : b;
        }
        int m = (l + r) >> 1;
        if(b <= m) return find_left(g, b, (k<<1)+1, l, m, t);
        if(r <= b && g(F(node[k], t))){
            t = F(node[k], t);
            return l;
        }
        int R = find_left(g, b, (k<<1)+2, m, r, t);
        if(m < R) return R;
        int L = find_left(g, b, (k<<1)+1, l, m, t);
        return min(L, R);
    }
};

int N, Q;
using P = pair<mint<MOD>, int>;
LazySegmentTree<P,int> st;
mint<MOD> memo1[400000], memo2[400000];

signed main(){
    cin >> N >> Q;

    memo1[0] = 1;
    for(int i=1; i<2*N; i++) memo1[i] = memo1[i-1] * 10;
    memo2[0] = 0;
    for(int i=1; i<2*N; i++) memo2[i] = memo2[i-1] * 10 + 1;
    st.init(vector<P>(N,mp(0,1)), [](P l, P r){ return mp( l.first + r.first * memo1[l.second], l.second + r.second ); }, mp(0,0),
        [](P m, int f){ return (f == -1 ? m : mp( memo2[m.second]*f, m.second)); }, [](int o, int n){ return (n == -1 ? o : n); }, -1);
    st.apply(0, N, 1);

    rep(i, Q){
        int l, r, d;
        cin >> l >> r >> d;
        st.apply(N-r, N-l+1, d);
        cout << st.query(0, N).first << endl;
    }
}

Measured memory_bytes: 22776