#include <iostream>
#include <string>
#include <vector>
#include <array>
#include <queue>
#include <deque>
#include <algorithm>
#include <set>
#include <map>
#include <bitset>
#include <cmath>
#include <functional>
#include <cassert>
#include <iomanip>
#define vll vector<ll>
#define vvvl vector<vvl>
#define vvl vector<vector<ll>>
#define VV(a, b, c, d) vector<vector<d>>(a, vector<d>(b, c))
#define VVV(a, b, c, d) vector<vvl>(a, vvl(b, vll (c, d)));
#define re(c, b) for(ll c=0;c<b;c++)
#define all(obj) (obj).begin(), (obj).end()
typedef long long int ll;
typedef long double ld;
using namespace std;

#define P 998244353
struct BIT{
  ll M=1;
  vector<ll> dat;
  BIT(ll N){
    if(N<=1) N = 2;
    while(N>M) M*=2;
    dat.resize(M*2-1, 0);
  }
  void update(ll x, ll k){
    for(int i=k+1;i<=M;i+=(i&(-i))){
      dat[i] = (dat[i] + x)%P;
    }
  }
  ll sum(ll r){
    ll ret = 0;
    for(int k=r;k>0;k-=(k&(-k))) ret = (ret + dat[k])%P;
    return ret;
  }
  ll query(ll l, ll r){
    return (sum(r) - sum(l) + P)%P;
  }
};

template <typename T>
struct SegmentTree{
  typedef array<ll, 3> pi;
  typedef function<T(T, T)> F; //merge
  typedef function<T(pi)> G; //[l,r) -> T を返す

  vector<T> dat;
  vector<pi> lazy;
  int M;
  F f;
  G g;
  T d1;//datの零元

  SegmentTree(int n, F f, G g, T d1, vector<T> v=vector<T>()): f(f), g(g), d1(d1){
    M = 1;
    while(M<n) M*=2;
    dat.clear();
    lazy.clear();
    dat.resize(2*M-1, d1);
    lazy.resize(2*M-1, {-1, -1, -1});
    if(n==(int)v.size()){
      for(int i=0;i<n;i++) dat[i+M-1] = v[i];
      for(int i=M-2;i>=0;i--) dat[i] = f(dat[i*2+1], dat[i*2+2]);
    }
  }
  inline void eval(int l, int r, int k){
    if(lazy[k][0]==-1) return;
    //std::cout << l << " " << r << " " << lazy[k][0] << " " << lazy[k][1] << '\n';
    assert(r-l==lazy[k][1]-lazy[k][0]);
    if(k*2+1<M*2-1){
      pi x = lazy[k];
      ll mid = (x[0] + x[1])/2;
      lazy[k*2+1] = pi{x[0], mid, x[2]};
      lazy[k*2+2] = pi{mid, x[1], x[2]};
    }
    dat[k] = g(lazy[k]);
    lazy[k] = pi{-1, -1, -1};
  }

  T update(int a, int b, pi x, int k, int l, int r){
    eval(l, r, k);
    if(x[0]>=x[1]||x[0]==-1||r<=a||b<=l) return dat[k];
    if(a<=l&&r<=b){
      lazy[k] = x;
      return g(x);
    }
    //右と左に分割する時xも分ける
    //区間の中間をmidとして左と右にいくつ要素が含まれるか
    //std::cout <<"!" << l << " " << r << " " << a << " " << b << " " << x[0] << " " << x[1] << '\n';

    ll mid = (l+r)/2;
    int LeftElement = mid - x[0];
    pi L, R;
    if(LeftElement <= 0){
      L = pi{-1, -1, -1};
      R = x;
    }else{
      L = pi{x[0], min(x[0]+LeftElement, x[1]), x[2]};
      R = pi{x[0] + LeftElement, x[1], x[2]};
    }
    //std::cout << "?" << L[0] << " " << L[1] << " " << R[0] << " " << R[1] << '\n';
    return dat[k] = f(update(a, b, L, k*2+1, l, mid), update(a, b, R, k*2+2, mid, r));
  }
  T query(int a, int b, int k, int l, int r){
    eval(l, r, k);
    if(r<=a||b<=l) return d1;
    if(a<=l&&r<=b) return dat[k];
    T vl = query(a, b, k*2+1, l, (l+r)/2);
    T vr = query(a, b, k*2+2, (l+r)/2, r);
    return f(vl, vr);
  }
  T update(int a,int b, pi x){return update(a,b,x,0,0,M);}
  T query(int a,int b){return query(a,b,0,0,M);}
};


/*
  区間数列更新
  segtree<T> (int n, F f, G g, T d1, vector<T>)
  F := 使用する関数(min/max/sumなど)
  G := 数列Aの[l, r)に対しF(A[l, r))を返す関数
*/

int main(){
  ll n, q;std::cin >> n >> q;
  BIT bt(n);
  ll c = 1;
  for(int i=0;i<n;i++) {
    bt.update(c, n-1-i);
    c = (c * 10LL)%P;
  }

  SegmentTree<ll> seg(n,
  [&](ll l, ll r){return (l+r)%P;},
  [&](array<ll, 3> r){
    ll q = bt.query(r[0], r[1]);
    return (q * r[2])%P;
  }, 0, vector<ll>(n, 0));


  seg.update(0, n, {0, n, 1});
  for(int i=0;i<q;i++){
    ll a, b, c;scanf("%lld %lld %lld", &a, &b, &c);
    a--;

    ll x = seg.query(a, b);
    //for(ll j=0;j<10;j++) std::cout << (x*j)%P << (j==9?"\n":" ");

    seg.update(a, b, {a, b, c});
    //ll y = seg.query(a, b);
    //std::cout << y << '\n';
    std::cout << seg.query(0, n) << '\n';
  }
}


Measured memory_bytes: 25380