#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <numeric>
#include <utility>
#include <tuple>

#define REP(i, a, b) for (int i = int(a); i < int(b); i++)
using namespace std;
using ll = long long int;

// clang-format off
#ifdef _DEBUG_
#define dump(...) do{ cerr << __LINE__ << ":\t" << #__VA_ARGS__ << " = "; PPPPP(__VA_ARGS__); cerr << endl; } while(false)
template<typename T> void PPPPP(T t) { cerr << t; }
template<typename T, typename... S> void PPPPP(T t, S... s) { cerr << t << ", "; PPPPP(s...); }
#else
#define dump(...) do{ } while(false)
#endif
template<typename T> vector<T> make_v(size_t a, T b) { return vector<T>(a, b); }
template<typename... Ts> auto make_v(size_t a, Ts... ts) { return vector<decltype(make_v(ts...))>(a, make_v(ts...)); }
template<typename T> bool chmin(T &a, T b) { if (a > b) {a = b; return true; } return false; }
template<typename T> bool chmax(T &a, T b) { if (a < b) {a = b; return true; } return false; }
template<typename T> void print(T a) { cout << a << endl; }
template<typename T, typename... Ts> void print(T a, Ts... ts) { cout << a << ' '; print(ts...); }
template<typename T> istream &operator,(istream &in, T &t) { return in >> t; }
// clang-format on

template<ll MOD = 1000000007>
class ModInt {
    ll n;
    ModInt constexpr inverse() const {
        return ModInt::pow(*this, MOD - 2);
    }

public:
    ModInt()
        : n(0) {}
    ModInt(ll _n)
        : n(((_n % MOD) + MOD) % MOD) {}
    ModInt operator+=(const ModInt &m) {
        n += m.n;
        if (n >= MOD) n -= MOD;
        return *this;
    }
    ModInt operator-=(const ModInt &m) {
        n -= m.n;
        if (n < 0) n += MOD;
        return *this;
    }
    ModInt operator*=(const ModInt &m) {
        n *= m.n;
        if (n >= MOD) n %= MOD;
        return *this;
    }
    ModInt operator/=(const ModInt &m) {
        (*this) *= m.inverse();
        return *this;
    }
    friend ModInt operator+(ModInt t, const ModInt &m) {
        return t += m;
    }
    friend ModInt operator-(ModInt t, const ModInt &m) {
        return t -= m;
    }
    friend ModInt operator*(ModInt t, const ModInt &m) {
        return t *= m;
    }
    friend ModInt operator/(ModInt t, const ModInt &m) {
        return t /= m;
    }
    ModInt operator=(const ll l) {
        n = l % MOD;
        if (n < 0) n += MOD;
        return *this;
    }
    friend ostream &operator<<(ostream &out, const ModInt &m) {
        out << m.n;
        return out;
    }
    friend istream &operator>>(istream &in, ModInt &m) {
        ll l;
        in >> l;
        m = l;
        return in;
    }
    static constexpr ModInt pow(const ModInt x, ll p) {
        ModInt<MOD> ans = 1;
        for (ModInt<MOD> m = x; p > 0; p /= 2, m *= m) {
            if (p % 2) ans *= m;
        }
        return ans;
    }
};
using mint = ModInt<998244353>;
mint operator"" _m(unsigned long long m) {
    return mint(m);
}

#include <functional>
template<typename T, typename S>
class LazySegmentTree {
    using Func1 = function<T(T, T)>;
    using Func2 = function<T(T, S)>;
    using Func3 = function<S(S, S)>;

private:
    vector<T> data;
    vector<S> lazy;
    T init1;
    S init2;
    int n;
    Func1 query_func;
    Func2 update_func;
    Func3 lazy_func;
    int depth;
    void _lazy_update(int node) {
        _update(node * 2 + 1, lazy[node]);
        _update(node * 2 + 2, lazy[node]);
        lazy[node] = init2;
    }
    void _update(int node, S val) {
        data[node] = update_func(data[node], val);
        if (node < n - 1) {
            lazy[node] = lazy_func(val, lazy[node]);
        }
    }
    void _lazy_update_all(int l, int r) {
        for (int i = depth, ln = l + n, rn = r + n; i >= 1; i--) {
            if (ln >> i << i != ln) {
                _lazy_update((ln >> i) - 1);
            }
            if (rn >> i << i != rn) {
                _lazy_update((rn >> i) - 1);
            }
        }
    }

public:
    LazySegmentTree(int _n, T _init1, S _init2, Func1 _query_func, Func2 _update_func, Func3 _lazy_func, vector<T> vinit) {
        init1 = _init1;
        init2 = _init2;
        query_func = _query_func;
        update_func = _update_func;
        lazy_func = _lazy_func;
        for (n = 1, depth = 0; n < _n; n *= 2, depth++)
            ;
        data.resize(2 * n - 1, init1);
        lazy.resize(n - 1, init2);
        for (int i = 0; i < vinit.size(); i++) {
            data[i + n - 1] = vinit[i];
        }
        for (int i = n - 2; i >= 0; i--) {
            data[i] = query_func(data[i * 2 + 1], data[i * 2 + 2]);
        }
    }
    void update(int l, int r, S val) {
        if (l == r) return;
        _lazy_update_all(l, r);
        for (int ln = l + n - 1, rn = r + n - 1; ln < rn; ln = ln / 2, rn = (rn - 1) / 2) {
            if (!(ln & 1)) {
                _update(ln, val);
            }
            if (!(rn & 1)) {
                _update(rn - 1, val);
            }
        }
        for (int i = 1, ln = l + n, rn = r + n; i <= depth; i++) {
            if (ln >> i << i != ln) {
                int p = (ln >> i) - 1;
                data[p] = query_func(data[p * 2 + 1], data[p * 2 + 2]);
            }
            if (rn >> i << i != rn) {
                int p = (rn >> i) - 1;
                data[p] = query_func(data[p * 2 + 1], data[p * 2 + 2]);
            }
        }
    }
    T query(int l, int r) {
        T resL = init1, resR = init1;
        _lazy_update_all(l, r);
        for (int ln = l + n - 1, rn = r + n - 1; ln < rn; ln = ln / 2, rn = (rn - 1) / 2) {
            if (!(ln & 1)) {
                resL = query_func(resL, data[ln]);
            }
            if (!(rn & 1)) {
                resR = query_func(data[rn - 1], resR);
            }
        }
        return query_func(resL, resR);
    }
};

using T = tuple<int, int, mint>;
using P = pair<int, int>;

int main() {
    cin.tie(nullptr);
    ios::sync_with_stdio(false);
    int n, q;
    cin, n, q;
    vector<mint> d(n + 1, 0_m);
    REP(i, 0, n) {
        d[n - i - 1] = d[n - i] * 10 + 1;
    }
    auto query = [](T a, T b) {
        auto [l1, r1, m1] = a;
        auto [l2, r2, m2] = b;
        return T(min(l1, l2), max(r1, r2), m1 + m2);
    };
    auto update = [&](T a, P b) {
        if (b.first == -1) return a;
        auto [l, r, m] = a;
        if (l > r) return a;
        return T(l, r, b.second * (d[l] - d[r]));
    };
    auto lazy = [](P a, P b) {
        return a.first < b.first ? b : a;
    };
    const int SZ = 200020;
    vector<T> vinit(n);
    REP(i, 0, n) {
        vinit[i] = T(i, i + 1, d[i] - d[i + 1]);
    }
    LazySegmentTree<T, P> seg(n, T(SZ, -1, 0), P(-1, 0), query, update, lazy, vinit);
    REP(i, 0, q) {
        int l, r, t;
        cin, l, r, t;
        seg.update(l - 1, r, P(i, t));
        print(get<2>(seg.query(0, n)));
    }

    return 0;
}


Measured memory_bytes: 21420