import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.PrintWriter
import java.util.*
import kotlin.math.min

fun main(args: Array<String>) {

    // IO Templates
    val br = BufferedReader(InputStreamReader(System.`in`))
    val out = PrintWriter(System.out)

    val (n, q) = br.readLine()!!.split(' ').map { it.toInt() }

    val sgt = RuqSegmentTree(n)

    for (i in 1..q) {
        val (l, r, d) = br.readLine()!!.split(' ').map { it.toInt() }
        sgt.update(l - 1, r, d.toLong())
        out.println(sgt.query(0, n))
    }

    out.flush()
}

/**
 * RUQ(遅延評価を加えた区間の最小値を求めるセグ木)
 * 区間更新ができる
 *
 * @property n セグ木のサイズ
 * @property tree セグ木本体
 * @property lazy 遅延評価用配列
 */
class RuqSegmentTree(
    val n_: Int
) {
    // companionにしても良いが、提出環境でバグる可能性があるのでこのまま
    private val INF: Long = 1_000_000_000_000_000_000L
    private val MOD: Long = 998_244_353L

    var n = 1
    var tree = longArrayOf()
    var lazy = longArrayOf()
    var coef = longArrayOf()

    init {
        n = 1
        while (n < n_) {
            n *= 2
        }

        coef = LongArray(2 * n - 1) { 0 }
        var cnt = n + n_ - 2
        var tens = 1L
        while (cnt >= n - 1) {
            coef[cnt] = tens
            tens *= 10
            tens %= MOD
            cnt--
        }
        while (cnt >= 0) {
            coef[cnt] = (coef[2 * cnt + 1] + coef[2 * cnt + 2]) % MOD
            cnt--
        }

        tree = LongArray(2 * n - 1) { coef[it] }
        lazy = LongArray(2 * n - 1) { INF }
    }

    fun update(a: Int, b: Int, x: Long, k: Int, l: Int, r: Int) {
        eval(k)
        if (a <= l && r <= b) {  // 完全に内側の時
            lazy[k] = x
            eval(k)
        } else if (a < r && l < b) {                     // 一部区間が被る時
            update(a, b, x, k * 2 + 1, l, (l + r) / 2) // 左の子
            update(a, b, x, k * 2 + 2, (l + r) / 2, r) // 右の子
            tree[k] = (tree[k * 2 + 1] + tree[k * 2 + 2]) % MOD
        }
    }

    fun update(a: Int, b: Int, x: Long) {
        update(a, b, x, 0, 0, n)
    }

    /**
     * [begin, end)の最小値を取得する
     *
     * @param begin 区間初め（閉区間）
     * @param end 区間終わり（開区間）
     * @return 区間の最小値
     */
    fun query(begin: Int, end: Int) = query_sub(begin, end, 0, 0, n)

    /**
     * 最小値取得用のサブクエリ
     *
     * @param begin 元の区間の初め（閉区間）
     * @param end 元の区間の終わり（開区間）
     * @param k 今見ているノードの位置
     * @param l tree[k]が表す区間の左端（閉区間）
     * @param r tree[k]が表す区間の右端（開区間）
     * @return 区間の最小値（区間外ならINF）
     */
    fun query_sub(begin: Int, end: Int, k: Int, l: Int, r: Int): Long {
        eval(k)
        return if (r <= begin || end <= l) { // 範囲外なら考えない
            0L
        } else if (begin <= l && r <= end) { // 範囲内なので自身の値を返す
            tree[k]
        } else { // 一部区間が被る時
            val vl = query_sub(begin, end, k * 2 + 1, l, (l + r) / 2)
            val vr = query_sub(begin, end, k * 2 + 2, (l + r) / 2, r)
            (vl + vr) % MOD
        }
    }

    fun eval(k: Int) { // 配列のk番目を更新
        if (lazy[k] == INF) return  // 更新するものが無ければ終了
        if (k < n - 1) {             // 葉でなければ子に伝搬
            lazy[k * 2 + 1] = lazy[k]
            lazy[k * 2 + 2] = lazy[k]
        }
        // 自身を更新
        tree[k] = (lazy[k] * coef[k]) % MOD
        lazy[k] = INF
    }
}

Measured memory_bytes: 71636