/*
g++ --std=c++17 E.cpp
*/

#include <iostream>
#include <iomanip> //! for setprecision(20)
#include <math.h>
#include <algorithm>
#include <functional> 
#include <string>
#include <vector>

#include <cstring>
#include <set>
#include <map>
#include <queue>
#include <utility>
#include <limits.h>
#include <atcoder/all>

using namespace std;
using namespace atcoder;

typedef long long LL;

#define rep(i,n) for (int i = 0; i < int(n); ++i)
#define dump(c) { for (auto it = c.begin(); it != c.end(); ++it) if (it == c.begin()) cout << *it; else cout << ' ' << *it; cout << endl; } 
#define dumpMap(m) { for (auto it: m) cout << it.first << "=>" << it.second << ' '; }

const int INF = 1000000000;
const int MOD = 998244353;
using mint = static_modint<MOD>;

/*
  == AC Library Cheat sheet
  documentation: file:///Users/nobu/Downloads/ac-library/document_ja/index.html

  mint
    mint m.pow(int p)  //! return m^p
    mint m.inv() //! returns i that gives (m * i).val() == 1 
    int m.val() 

  fenwick_tree (BIT)
    fenwick_tree<T> fw(int n) //! init a[0] .. a[n-1] with all 0
    void fw.add(int idx, T x); //! a[idx] += x
    T fw.sum(int l, int r); //! return a[l] + .. + a[r-1]

  dsu (UnionFind)
    dsu d(int n)  //! prepare dsu with n nodes
    void d.merge(int x, int y)  //! connect node x and y
    bool d.same(int x, int y)  //! return true if node x and y are connected
    int d.leader(int x)  //! return the leader node of the connected group
    int d.size(int x) //! return the size of the group that node x belongs to
    vector<vector<int>> d.groups() //! return a vector of vectors that contain the nodes in each group

  scc_graph
    scc_graph　g(int n)  //! create a directed graph with n nodes
    g.add_edge(int from, int to)  //! create a directed edge from node from to node to
    vector<vector<int>> g.scc()  //! return the vector of strongly connected components that are topologically sorted

  segtree
    segtree<S, op, e>
      S: type of the monoid 
      op: function to return the product of two elements
      e: function to return the identity element such that op(x, e) == x fo any x 

  lazy_segtree
    lazy_segtree<S, op, e, F, mapping, composition, id>
      F: type of parameters to define the operation applied to the target elements
      mapping: function to return the element after applying the operation to the target element
      composition: function to combine the two sets of operation parameters to one
      id: function to return the operation parameter i such that mapping(i, x) = x for any x

      using S = int;
      S op(S a, S b) { return min(a, b); }
      S e() { return INF; }
      using F = int;
      S mapping(F f, S x) { return min(f, x); }
      F composition(F f, F g) { return min(f, g); }
      F id() { return INF; }

  floor_sum(ll n, ll m, ll a, ll b)   
*/


mint tens[200005], ones[200005];

void init()
{
  tens[0] = mint(1);
  ones[0] = mint(0);
  for (int i = 1; i <= 200000; ++i) {
    tens[i] = tens[i-1] * 10;
    ones[i] = ones[i-1] * 10 + 1;
  }
}

struct S {
  S(const mint& v=0, int l=0) : val(v), len(l) {}
  mint val;
  int len;
};

S op(S lhs, S rhs) {
  return S(lhs.val * tens[rhs.len] + rhs.val, lhs.len + rhs.len);
}

S e() {
  return S();
}

using F = int;

S mapping(F f, S x) { 
  return f ? S(f * ones[x.len], x.len) : x; 
}

F composition(F f, F g) { 
  return f ? f : g;
}

F id() { 
  return 0; 
}

int main()
{
  cin.tie(0);
  ios::sync_with_stdio(0);

  init();

  int n, q;
  cin >> n >> q;

  lazy_segtree<S, op, e, F, mapping, composition, id> st(n);
  rep(i, n) st.set(i, S(1,1));

  rep(i, q) {
    int l, r, d;
    cin >> l >> r >> d;
    --l;
    st.apply(l, r, d);
    cout << st.all_prod().val.val() << ' ';
  }
  cout << endl;

  return 0;
}


Measured memory_bytes: 11344