#include <bits/stdc++.h>

using namespace std;

#define eb emplace_back
#define ii pair<int, int>
#define OK (cerr << "OK" << endl)
#define debug(x) cerr << #x " = " << (x) << endl
#define ff first
#define ss second
#define int long long
#define tt tuple<int, int, int>
#define all(x) x.begin(), x.end()
#define vi vector<int>
#define vii vector<pair<int, int>>
#define vvi vector<vector<int>>
#define vvii vector<vector<pair<int, int>>>
#define Mat(n, m, v) vector<vector<int>>(n, vector<int>(m, v))
#define endl '\n'

constexpr int INF = numeric_limits<int>::max() / 4;
constexpr int MOD = 998244353;
constexpr int MAXN = 2e5 + 3;

vi ps(MAXN);

int mod(int n) {
  n %= MOD;
  if (n < 0)
    n += MOD;
  return n;
}

int query_ps(int l, int r) {
  if (l == 0)
    return ps[r];
  else
    return mod(ps[r] - ps[l - 1]);
}

class Seg_Tree {
public:
  struct Node {
    int val, lazy;

    Node() {}
    Node(const int val) : val(val), lazy(0) {}
  };

private:
  // Range Sum
  Node NEUTRAL_NODE = Node(0);
  Node merge_nodes(const Node &x, const Node &y) {
    return Node(mod(x.val + y.val));
  }
  void apply_lazy(const int l, const int r, const int pos) {
    // for set change this to =
    tree[pos].val = mod(query_ps(l, r) * tree[pos].lazy);
  }

private:
  int n;

public:
  vector<Node> tree;

private:
  void propagate(const int l, const int r, const int pos) {
    if (tree[pos].lazy != 0) {
      apply_lazy(l, r, pos);
      if (l != r) {
        // for set change this to =
        tree[2 * pos + 1].lazy = tree[pos].lazy;
        tree[2 * pos + 2].lazy = tree[pos].lazy;
      }
      tree[pos].lazy = 0;
    }
  }

  Node _build(const int l, const int r, const vector<int> &arr, const int pos) {
    if (l == r)
      return tree[pos] = Node(arr[l]);

    int mid = (l + r) / 2;
    return tree[pos] = merge_nodes(_build(l, mid, arr, 2 * pos + 1),
                                   _build(mid + 1, r, arr, 2 * pos + 2));
  }

  int _get_first(const int l, const int r, const int i, const int j,
                 const int v, const int pos) {
    propagate(l, r, pos);

    if (l > r || l > j || r < i)
      return -1;
    // Needs RMQ MAX
    // Replace to <= for greater or (with RMQ MIN) > for smaller or
    // equal or >= for smaller
    if (tree[pos].val < v)
      return -1;

    if (l == r)
      return l;

    int mid = (l + r) / 2;
    int aux = _get_first(l, mid, i, j, v, 2 * pos + 1);
    if (aux != -1)
      return aux;
    return _get_first(mid + 1, r, i, j, v, 2 * pos + 2);
  }

  Node _query(const int l, const int r, const int i, const int j,
              const int pos) {
    propagate(l, r, pos);

    if (l > r || l > j || r < i)
      return NEUTRAL_NODE;

    if (i <= l && r <= j)
      return tree[pos];

    int mid = (l + r) / 2;
    return merge_nodes(_query(l, mid, i, j, 2 * pos + 1),
                       _query(mid + 1, r, i, j, 2 * pos + 2));
  }

  // It adds a number val to the range from i to j
  Node _update(const int l, const int r, const int i, const int j,
               const int val, const int pos) {
    propagate(l, r, pos);

    if (l > r || l > j || r < i)
      return tree[pos];

    if (i <= l && r <= j) {
      tree[pos].lazy = val;
      propagate(l, r, pos);
      return tree[pos];
    }

    int mid = (l + r) / 2;
    return tree[pos] = merge_nodes(_update(l, mid, i, j, val, 2 * pos + 1),
                                   _update(mid + 1, r, i, j, val, 2 * pos + 2));
  }

  void build(const vector<int> &arr) {
    this->tree.resize(4 * this->n);
    this->_build(0, this->n - 1, arr, 0);
  }

public:
  /// N equals to -1 means the Segment Tree hasn't been created yet.
  Seg_Tree() : n(-1) {}

  /// Constructor responsible for initializing the tree with 0's.
  ///
  /// Time Complexity O(n)
  Seg_Tree(const int n) : n(n) { this->tree.resize(4 * this->n, Node(0)); }

  /// Constructor responsible for building the tree based on a vector.
  ///
  /// Time Complexity O(n)
  Seg_Tree(const vector<int> &arr) : n(arr.size()) { this->build(arr); }

  /// Returns the first index from i to j compared to v.
  /// Uncomment the line in the original function to get the proper element that
  /// may be: GREATER OR EQUAL, GREATER, SMALLER OR EQUAL, SMALLER.
  ///
  /// Time Complexity O(log n)
  int get_first(const int i, const int j, const int v) {
    assert(this->n >= 0);
    return this->_get_first(0, this->n - 1, i, j, v, 0);
  }

  /// Update at a single index.
  ///
  /// Time Complexity O(log n)
  void update(const int idx, const int val) {
    assert(this->n >= 0);
    assert(0 <= idx), assert(idx < this->n);
    this->_update(0, this->n - 1, idx, idx, val, 0);
  }

  /// Range update from l to r.
  ///
  /// Time Complexity O(log n)
  void update(const int l, const int r, const int val) {
    assert(this->n >= 0);
    assert(0 <= l), assert(l <= r), assert(r < this->n);
    this->_update(0, this->n - 1, l, r, val, 0);
  }

  /// Query at a single index.
  ///
  /// Time Complexity O(log n)
  int query(const int idx) {
    assert(this->n >= 0);
    assert(0 <= idx), assert(idx < this->n);
    return this->_query(0, this->n - 1, idx, idx, 0).val;
  }

  /// Range query from l to r.
  ///
  /// Time Complexity O(log n)
  int query(const int l, const int r) {
    assert(this->n >= 0);
    assert(0 <= l), assert(l <= r), assert(r < this->n);
    return this->_query(0, this->n - 1, l, r, 0).val;
  }
};

// #define MULTIPLE_TEST_CASES
void solve() {
  int n;
  cin >> n;
  int q;
  cin >> q;

  for (int i = 0; i < n; ++i)
    if (i == 0)
      ps[i] = 1;
    else
      ps[i] = mod(10 * ps[i - 1] + 1);

  Seg_Tree st(n);
  st.update(0, n - 1, 1);
  // debug(st.query(0, n - 1));

  while (q--) {
    int l, r, d;
    cin >> l >> r >> d;
    l = n - l, r = n - r;
    st.update(r, l, d);
    cout << st.query(0, n - 1) << endl;
  }
}

signed main() {
  // freopen(".in", "r", stdin);
  // freopen(".out", "w", stdout);
  ios_base::sync_with_stdio(false);
  cin.tie(nullptr), cout.tie(nullptr);

  int t = 1;
#ifdef MULTIPLE_TEST_CASES
  cin >> t;
#endif
  while (t--)
    solve();
}

Measured memory_bytes: 17180