#include <bits/stdc++.h>
using namespace std;
#define MAXN 1000010
#define optimizar_io ios_base::sync_with_stdio(false);cin.tie(0);
typedef long long int ll;

ll arr[MAXN], acum[MAXN];

ll mod(ll a, ll b = 998244353) {
	return ((a % b) + b) % b;
}

ll powMod(ll a, ll n, ll m = 998244353) {
	ll result = 1;
	while (n) {
		if (n & 1)
			result = mod(result * a, m);
		a = mod(a * a, m);
		n >>= 1;
	}
	return result;
}

ll modInv(ll a, ll n = 998244353) {
	return powMod(a, n - 2);
}

ll geometrica(ll x, ll n) {
	ll ans = powMod(x, n + 1);
	ans--;
	ll inv = modInv(x - 1);
	ll power_sum = mod(ans * inv);
	return power_sum;
}

ll range(int l, int r) {
	if (l == 0)
		return acum[r];
	else
		return mod(acum[r] - acum[l - 1]);
}

struct Node {
	ll sum;
	
	void make_Node(ll v, int idx) {
    	sum = mod(v * powMod(10, idx));
	}

	void merge(Node left, Node right) {
		sum = mod(left.sum + right.sum);
	}
	
	ll getValue() {
		return sum;
	}
};

struct SegmentTree {
	
	Node *stree;
    vector<ll> lazy;

	SegmentTree(int N) {
		stree = new Node[getSegmentTreeSize(N)];
        lazy.assign(getSegmentTreeSize(N), 0);
        buildTree(1, 0, N - 1);
	}

	void buildTree(int nodo, int L, int R) {
		if (L == R)
			stree[nodo].make_Node(arr[L], L);
		else {
			int mid = (L + R) / 2, leftIdx = 2 * nodo, rightIdx = 2 * nodo + 1;
			buildTree(leftIdx, L, mid);
			buildTree(rightIdx, mid + 1, R);
			stree[nodo].merge(stree[leftIdx], stree[rightIdx]);
		}
	}

	void propagate(int nodo, int L, int R) {
		int leftIdx = 2 * nodo, rightIdx = 2 * nodo + 1;
        if (lazy[nodo] != 0) {
        	stree[nodo].sum = mod(lazy[nodo] * range(L, R));
        	if (L != R) {
                lazy[leftIdx] = lazy[nodo];
                lazy[rightIdx] = lazy[nodo];
            }
            lazy[nodo] = 0;
        }
	}

	Node query(int nodo, int L, int R, int i, int j) {
		propagate(nodo, L, R);
		if (L >= i && R <= j)
			return stree[nodo];
		
		int mid = (L + R) / 2, leftIdx = 2 * nodo, rightIdx = 2 * nodo + 1;
		if (j <= mid)
			return query(leftIdx, L, mid, i, j);
		if (i > mid)
			return query(rightIdx, mid + 1, R, i, j);
        
		Node leftResult = query(leftIdx, L, mid, i, mid);
		Node rightResult = query(rightIdx, mid + 1, R, mid + 1, j);
		Node result;
		result.merge(leftResult, rightResult);
		return result;
	}

	void updateRange(int nodo, int low, int high, int startRange, int endRange, ll value) {
        propagate(nodo, low, high);
        if (low > high || startRange > high || endRange < low) {
        	return;
        }
		int mid = (low + high) / 2, leftIdx = 2 * nodo, rightIdx = 2 * nodo + 1;
		if (startRange <= low && endRange >= high) {
			stree[nodo].sum = mod(value * range(low, high));
			if (low != high) {
                lazy[leftIdx] = value;
                lazy[rightIdx] = value;
            }
            return;
		}
        updateRange(leftIdx, low, mid, startRange, endRange, value);
        updateRange(rightIdx, mid + 1, high, startRange, endRange, value);
		stree[nodo].merge(stree[leftIdx], stree[rightIdx]);
	}

	int getSegmentTreeSize(int N) {
		int size = 1;
		for(; size < N; size <<= 1);
		return size << 1;
	}
};

int main() {
	optimizar_io
	int N, Q, l, r, d;
    
    cin >> N >> Q;
    acum[0] = 1;
    for (int i = 1; i <= N; i++) {
    	acum[i] = mod(acum[i - 1] + powMod(10, i));
    }
    fill(arr, arr + N, 1);
	SegmentTree st(N);
	while (Q--) {
       	cin >> l >> r >> d;
       	l--; r--;
       	l = N - l - 1;
       	r = N - r - 1;
       	if (r < l) swap(l, r);
       	st.updateRange(1, 0, N - 1, l, r, d);
        Node q = st.query(1, 0, N - 1, 0, N - 1);
        cout << q.getValue() << "\n";
    }
    return 0;
}

Measured memory_bytes: 14524