#include <iostream>
#include <iomanip>
#include <algorithm>
#include <array>
#include <cassert>
#include <optional>
#include <utility>
#include <vector>
#include <atcoder/all>

template <class InputIterator>
std::ostream& range_output(std::ostream& os_arg, InputIterator first_arg, InputIterator last_arg){ if(first_arg != last_arg){ do{ os_arg << *(first_arg++); if(first_arg == last_arg) break; os_arg << ' '; } while(true); }  return os_arg; }
template <class Tp> std::ostream& operator << (std::ostream& os_arg, const std::vector<Tp>& arr_arg){ return range_output(os_arg, arr_arg.cbegin(), arr_arg.cend()); }
template <class Tp, std::size_t Size> std::ostream& operator << (std::ostream& os_arg, const std::array<Tp, Size>& arr_arg){ return range_output(os_arg, arr_arg.cbegin(), arr_arg.cend()); }
template <class S, class T> std::ostream& operator << (std::ostream& os_arg, const std::pair<S, T>& pair_arg){ return os_arg << '(' << pair_arg.first << ", " << pair_arg.second << ')'; }

#ifndef ONLINE_JUDGE
    template <typename Head> void dump_out(Head head_arg){ std::cerr << head_arg << '\n'; }
    template <typename Head, typename... Tail>
    void dump_out(Head head_arg, Tail... tail_args){ std::cerr << head_arg << ", "; dump_out(tail_args...); }
    #define dump(...) do { std::cerr << "[in line " << __LINE__ << "] " << #__VA_ARGS__ << " : "; dump_out(__VA_ARGS__); } while(false)
#else
    #define dump(...) (void(0))
#endif

// modint構造体
template <int modulus> struct mod_Int{
    int val; // 値本体
    // コンストラクタ
    constexpr mod_Int():val(0){}
    constexpr mod_Int(const int& v):val(v % modulus){ if(val < 0) val += modulus; }
    constexpr mod_Int(int&& v):val(v % modulus){ if(val < 0) val += modulus; }
    constexpr mod_Int(const long long int& v):val(v % modulus){ if(val < 0) val += modulus; }
    constexpr mod_Int(long long int&& v):val(v % modulus){ if(val < 0) val += modulus; }
    constexpr mod_Int(const unsigned int& v):val(v % modulus){}
    constexpr mod_Int(unsigned int&& v):val(v % modulus){}
    constexpr mod_Int(const unsigned long long int& v):val(v % modulus){}
    constexpr mod_Int(unsigned long long int&& v):val(v % modulus){}
    constexpr mod_Int& operator += (const mod_Int<modulus>& other) noexcept {
        val += other.val; if(val >= modulus) val -= modulus;
        return *this;
    }
    constexpr mod_Int& operator -= (const mod_Int<modulus>& other) noexcept {
        val -= other.val; if(val < 0) val += modulus;
        return *this;
    }
    constexpr mod_Int& operator *= (const mod_Int<modulus>& other) noexcept {
        val = static_cast<long long int>(val) * other.val % modulus;
        return *this; 
    }
    constexpr mod_Int& operator /= (const mod_Int<modulus>& other) noexcept {
        val = static_cast<long long int>(val) * other.inverse() % modulus;
        return *this;
    }
    constexpr bool operator == (const mod_Int<modulus>& other) const noexcept {
        return val == other.val;
    }
    constexpr mod_Int operator +(const mod_Int<modulus>& v) const noexcept{
        return mod_Int<modulus>(*this) += v;
    }
    constexpr mod_Int operator -(const mod_Int<modulus>& v)const noexcept{
        return mod_Int<modulus>(*this) -= v;
    }
    constexpr mod_Int operator *(const mod_Int<modulus>& v) const noexcept{
        return mod_Int<modulus>(*this) *= v;
    }
    constexpr mod_Int operator /(const mod_Int<modulus>& v) const noexcept{
        return mod_Int<modulus>(*this) /= v;
    }
    constexpr mod_Int& operator ++(void) noexcept {
        if(++val == modulus) val = 0;
        return *this;
    }
    constexpr mod_Int& operator --(void) noexcept {
        if(val-- == 0) val = modulus - 1;
        return *this;
    }
    constexpr mod_Int operator -()const noexcept{
        return mod_Int<modulus>((val == 0 ? 0 : modulus - val));
    }

    // aの逆元を求める関数
    static constexpr int inverse(int a) noexcept {
        int b = modulus, 
            u = 1, v = 0;
        while (b != 0) {
            const int t = a / b; 
            a -= t * b; std::swap(a, b);
            u -= t * v; std::swap(u, v);
        }
        if(u < 0) u += modulus;
        return u;
    }
    constexpr int inverse(void) const noexcept { return inverse(val); }
    // a^nを返す関数 : nは負の数でも可
    static constexpr int power(int a, long long int n){
        long long res = 1, waiting = a;
        if(n < 0) waiting = inverse(a), n = -n;
        while(n != 0){ 
            if(n % 2 != 0) res = res * waiting % modulus; 
            waiting = waiting * waiting % modulus; 
            n /= 2;
        }
        return res;
    }
    constexpr mod_Int power(long long int n) const noexcept { mod_Int res; res.val = power(val, n); return res; }
    friend std::ostream& operator << (std::ostream& os_arg, const mod_Int<modulus>& mod_Int_arg){
        os_arg << mod_Int_arg.val;
        return os_arg;
    }
};


constexpr int mod = 998'244'353;
using modint = mod_Int<mod>;



// @ 遅延伝搬セグメント木 (データ構造)
// * 区間更新, 区間取得 が O(logN) で達成可能である
// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
//    struct monoid_lazy_segment_tree{
//        using value_type = ;          // データの型
//        using lazy_type = ;           // 遅延評価作用素の型
//        inline static constexpr value_type identity_element = 0;  // データの単位元
//        // データ型の演算 (クエリの畳み込み和の演算) : 他ライブラリでは f などで書かれている
//        static value_type operation(const value_type& x, const value_type& y){
//            return ;
//        }
//        // 遅延評価を実際に行う演算 : (更新(評価)されるデータ型の値, 遅延作用素, その区間幅) → 新しく評価するデータの値
//        static lazy_type merge(const value_type& x, const lazy_type& y, const int N){
//            return ;
//        }
//        // 遅延作用素の伝搬 (古い作用素, 新しい作用素) → (作用素)
//        static lazy_type propagate(const lazy_type& x, const lazy_type& y){
//            return ;
//        }
//    };
// ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
template <class monoid_lazy_segment_tree> class LazySegmentTree{
private:
    using element_t   = typename monoid_lazy_segment_tree::value_type;
    using lazy_t      = typename monoid_lazy_segment_tree::lazy_type;
    using lazy_opt_t  = std::optional<lazy_t>;
    int height;              //  セグメント木の高さ : 要素数 2^k の k
    int length;              //  セグメント木の要素数 : 2^k (dataの数は 2 * 2^k)
    std::vector<element_t> data;   //  要素の部分
    std::vector<lazy_opt_t> lazy;  //  遅延評価部分

    // セグメント木に必要な高さを計算する補助関数
    static int make_height(const int n){
        int res = 0; for(int len = 1; len < n; ++res, len <<= 1);
        return res;
    }
    // 葉である頂点 x に向かって根からトップダウンに遅延評価部(lazy)を伝搬させていく関数
    inline void calculateData(int x){
        for(int len = 1; x >>= 1; len <<= 1){
            const int c1 = x << 1, c2 = c1 | 1;
            data[x] = monoid_lazy_segment_tree::operation(
                lazy[c1] ? monoid_lazy_segment_tree::merge(data[c1], *lazy[c1], c1) : data[c1],
                lazy[c2] ? monoid_lazy_segment_tree::merge(data[c2], *lazy[c2], c2) : data[c2]
            );
        }
    }
    // 葉である頂点 x から 根に向かってボトムアップに 要素本体 (data) の値を lazy を元に更新していく関数
    inline void propagate(int x){
        for(int shift = height, len = 1 << shift; shift ; --shift, len >>= 1){
            const int idx = x >> shift;
            if(lazy[idx]){
                const int idx1 = idx << 1;
                lazy[idx1] = lazy[idx1] ? lazy_opt_t(monoid_lazy_segment_tree::propagate(*lazy[idx1], *lazy[idx])) : lazy[idx];
                const int idx2 = (idx << 1) | 1;
                lazy[idx2] = lazy[idx2] ? lazy_opt_t(monoid_lazy_segment_tree::propagate(*lazy[idx2], *lazy[idx])) : lazy[idx];
                data[idx] = monoid_lazy_segment_tree::merge(data[idx], *lazy[idx], idx);
                lazy[idx] = std::nullopt;
            }
        }
        if(lazy[x]){ 
            data[x] = monoid_lazy_segment_tree::merge(data[x], *lazy[x], x); 
            lazy[x] = std::nullopt;
        }
    }

public:
    LazySegmentTree(void){}
    // コンストラクタ
    LazySegmentTree(const int len):height(make_height(len)), length(1 << height){
        data.assign(2 * length, monoid_lazy_segment_tree::identity_element);
        lazy.assign(2 * length, std::nullopt);
    }
    LazySegmentTree(const int len, const element_t& init_value):height(make_height(len)), length(1 << height){
        data.resize(2 * length); 
        std::fill(data.begin() + length, data.begin() + length + len, init_value);
        std::fill(data.begin() + length + len, data.end(), monoid_lazy_segment_tree::identity_element);
        for(int idx = length - 1; idx ; --idx) data[idx] = monoid_lazy_segment_tree::operation(data[idx << 1], data[(idx << 1) | 1]);
        lazy.assign(2 * length, std::nullopt);
    }
    LazySegmentTree(const std::vector<element_t>& A):height(make_height(A.size())), length(1 << height){
        lazy.assign(2 * length, std::nullopt);
        data.resize(2 * length); std::copy(A.begin(), A.end(), data.begin() + length);
        std::fill(data.begin() + length + A.size(), data.end(), monoid_lazy_segment_tree::identity_element);
        for(int idx = length - 1; idx ; --idx) data[idx] = monoid_lazy_segment_tree::operation(data[idx << 1], data[(idx << 1) | 1]);
    }
    template <class InputIterator>
    LazySegmentTree(InputIterator first, InputIterator last):height(make_height(last - first)), length(1 << height){
        lazy.assign(2 * length, std::nullopt);
        data.resize(2 * length); std::copy(first, last, data.begin() + length);
        std::fill(data.begin() + length + (last - first), data.end(), monoid_lazy_segment_tree::identity_element);
        for(int idx = length - 1; idx; --idx) data[idx] = monoid_lazy_segment_tree::operation(data[idx << 1], data[(idx << 1) | 1]);
    }

    // 半開区間 [l, r) (0-indexed) に作用素 val を作用させる関数
    inline void update_range(int l, int r, const lazy_t& val){
        if(l >= r) return ;
        propagate(l += length); propagate(r += length - 1);
        for(int li = l, ri = r + 1; li < ri; li >>= 1, ri >>= 1){
            if(li & 1){ lazy[li] = lazy[li] ? monoid_lazy_segment_tree::propagate(*lazy[li], val) : val; li++; }
            if(ri & 1){ --ri; lazy[ri] = lazy[ri] ? monoid_lazy_segment_tree::propagate(*lazy[ri], val) : val; }
        }
        calculateData(l); calculateData(r);
    }
    // 半開区間 [l, r) (0-indexed) 上の畳み込み和 al * al+1 * ... * ar-1 を求める関数
    inline element_t fold_range(int l, int r){
        if(l >= r) return monoid_lazy_segment_tree::identity_element;
        propagate(l += length); propagate((r += length) - 1);
        element_t vl = monoid_lazy_segment_tree::identity_element, vr = monoid_lazy_segment_tree::identity_element;
        int len = 1;
        while(l < r){
            if(l & 1){
                vl = monoid_lazy_segment_tree::operation( vl, lazy[l] ? monoid_lazy_segment_tree::merge(data[l], *lazy[l], l) : data[l] ); l++;
            }
            if(r & 1){
                --r; vr = monoid_lazy_segment_tree::operation(lazy[r] ? monoid_lazy_segment_tree::merge(data[r], *lazy[r], r) : data[r], vr);
            }
            l >>= 1; r >>= 1; len <<= 1;
        }
        return monoid_lazy_segment_tree::operation(vl, vr);
    }    
};


std::vector<modint> D;



struct monoid_lazy_segment_tree{
    using value_type = modint;          // データの型
    using lazy_type = bool;           // 遅延評価作用素の型
    inline static constexpr value_type identity_element = modint(0);  // データの単位元
    // データ型の演算 (クエリの畳み込み和の演算) : 他ライブラリでは f などで書かれている
    static value_type operation(const value_type& x, const value_type& y){
        return x + y;
    }
    // 遅延評価を実際に行う演算 : (更新(評価)されるデータ型の値, 遅延作用素, その区間幅) → 新しく評価するデータの値
    static value_type merge(const value_type& x, const lazy_type& y, const int i){
        return y ? D[i] : 0;
    }
    // 遅延作用素の伝搬 (古い作用素, 新しい作用素) → (作用素)
    static lazy_type propagate(const lazy_type& x, const lazy_type& y){
        return y;
    }
};

int len;

void build(int n){
    for(len = 1; len < n; len <<= 1);
    D.assign(2 * len, modint(0));
    const modint t(10); modint m(1);
    for(int i = n - 1; i >= 0; --i){ D[i + len] = m; m *= t; }
    for(int i = len - 1; i > 0; --i) D[i] = D[2 * i] + D[2 * i + 1];
}


int main(void){
    std::cin.tie(nullptr); std::ios_base::sync_with_stdio(false); 
    std::cout << std::fixed << std::setprecision(16);

    int n, q; std::cin >> n >> q;
    build(n);
    std::array<LazySegmentTree<monoid_lazy_segment_tree>, 9> ST;
    ST[0] = LazySegmentTree<monoid_lazy_segment_tree>(D.begin() + len, D.begin() + len + n);
    for(int i = 1; i < 9; ++i){ 
        ST[i] = LazySegmentTree<monoid_lazy_segment_tree>(n);
    }

    for(int _ = 0; _ < q; ++_){
        int l, r, d; std::cin >> l >> r >> d; --l;
        modint res(0);
        for(int x = 0; x < 9; ++x){
            ST[x].update_range(l, r, (x + 1) == d);
            const modint e(x + 1);
            res += e * ST[x].fold_range(0, n);
        }
        std::cout << res << '\n';
    }

    return 0;
}

Measured memory_bytes: 33068