// #pragma GCC optimize("Ofast")
// #pragma GCC optimize ("unroll-loops")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp> 
#include <ext/pb_ds/tree_policy.hpp> 
using namespace std;
using namespace __gnu_pbds;

typedef long long int ll;
#define endl '\n'
#define ld long double
#define all(a) a.begin(),a.end()
#define int long long
#define pb push_back
#define pii pair <int, int>
#define ff first
#define ss second
#define sz(v) (int)v.size() 
#define UB upper_bound
#define LB lower_bound
#define BP(x) __builtin_popcountll(x)
#define PQS priority_queue <int, vector<int>, greater<int> > 
#define OST tree<pii, null_type,less<pii>, rb_tree_tag,tree_order_statistics_node_update>
mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
int getRand(int l, int r) {
  uniform_int_distribution<int> uid(l, r);
  return uid(rng);
}

const int INF = 1e18 + 0;
//const int mod = 1e9 + 7;
const int mod = 998244353;

const int N = 2e5 + 5;
static int M;

int Data[N], po[N], pre[15][N];

class lazySegmentTree{
  public:
    vector <int> seg;
    vector <int> lazy;
    int dummy, dummyLazy;

    lazySegmentTree(int n, int dummy, int dummyLazy) {
      M = n;
      this->dummy = dummy;
      this->dummyLazy = dummyLazy;
      seg.resize(4*n+1, dummy);
      lazy.resize(4*n+1, dummyLazy);
    }
    
    void push(int node, int s, int e) {
       if(lazy[node] != 0) {
         seg[node] =  pre[lazy[node]][e-s+1];
         if(s != e) lazy[2*node] = lazy[node];
         if(s != e) lazy[2*node+1] = lazy[node];
         lazy[node] = 0;
       }
    }

    int merge(int x, int y, int l) {
        int k = x*po[l] + y;
        k %= mod;
        return k;
    }

    void build(int node = 1, int s = 1, int e = M) {
      if(s == e) {
        seg[node] = Data[s];
        return;
      }
      int m = (s + e) / 2;
      build(2*node, s, m);
      build(2*node+1, m+1, e);
      seg[node] = merge(seg[2*node], seg[2*node+1], e-m);
    }

    void update(int l, int r, int val, int node = 1, int s = 1, int e = M) {
      push(node, s, e);
      if(s > r || e < l) return;
      if(s == e) {
        seg[node] = val;
        return;
      }
      if(s >= l && e <= r) {
        seg[node] = pre[val][e-s+1];
        lazy[2*node] = val;
        lazy[2*node+1] = val;
        return;
      }
      int m = (s+e) / 2;
      update(l, r, val, 2*node, s, m);
      update(l, r, val, 2*node+1, m+1, e);
      seg[node] = merge(seg[2*node], seg[2*node+1], e-m);
    }

    int query(int l, int r, int node = 1, int s = 1, int e = M) {
      if(l > r) return 0;
      push(node, s, e);
      if(s > r || e < l) return dummy;
      if(s >= l && e <= r) return seg[node];
      int m = (s+e) / 2;
      return merge(query(l, r, 2*node, s, m), query(l, r, 2*node+1, m+1, e), e-m);
    }
};

void solve() {
  po[0] = 1;
  for(int i = 1; i <= N-5; i++) {
    po[i] = 10 * po[i-1];
    po[i] %= mod;
  }
  
  for(int i = 1; i <= 9; i++) {
    pre[i][1] = i;
    for(int j = 2; j <= N-5; j++) {
      pre[i][j] = 10 * pre[i][j-1] + i;
      pre[i][j] %= mod;
    }
  }

  int n,q;
  cin >> n >> q;
  
  for(int i = 1; i <= n; i++) Data[i] = 1;

  lazySegmentTree st(n,0,0);
  
  st.build();
  
  while(q--) {
    int l, r, k;
    cin >> l >> r >> k;
    st.update(l, r, k);
    int ans = st.query(1,n);
    cout << ans << '\n';
  }


}

/*
  * overflow, array bounds
  * do smth instead of nothing and stay organized
*/
signed main() {   
  ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);

  #ifndef ONLINE_JUDGE
   freopen("input.txt", "r", stdin);
   freopen("output.txt", "w", stdout);
  #endif
   int t = 1; // cin >> t;
   for(int i = 1; i <= t; i++) {
   //  cout << "Case #" << i << ": ";
     solve();
  }
}    

/*

*/

Measured memory_bytes: 33088