#include <iostream>
#include <string>
#include <sstream>
#include <iomanip> 
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <functional>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#include <bitset>
#include <complex>

using namespace std;

//#pragma GCC optimize("Ofast")
//#pragma GCC optimization("unroll-loops, no-stack-protector")
//#pragma GCC target("avx,avx2,fma")


#define MAXX 200010
#define MOD 998244353

vector<long long> f, s;


// segment tree with lazy propagation class

#define MAX_ST_N 1000500	// # of total possible nodes = initial N + log N * Query number

struct lst_node {
	long long val;				// sum for range sum query (or range min/max)
	long long offset;			// value added to this range
};

//lst_node node_list[MAX_ST_N * 4];

class lazy_segment_tree {
public:
	long long size;
	vector<lst_node> node_list;

	lazy_segment_tree() {
	}

	void initialize_empty(long long n) {		// initialize an empty segment tree
		size = n;
		lst_node lsd;
		lsd.val = 0;
		lsd.offset = 0;
		node_list.assign(size * 4 + 1000, lsd);	// stored in 1-based, left = p * 2, right = p * 2 + 1s
	}

	long long build_tree(long long l, long long r, long long p, vector<long long>& a) {		// returns node list
		if (l == r) {
			node_list[p].val = a[l];
			node_list[p].offset = 0;

			return p;		// return node #
		}
		else {
			long long m = (l + r) >> 1;
			build_tree(l, m, p * 2, a);
			build_tree(m + 1, r, p * 2 + 1, a);


			// range sum
			node_list[p].val = node_list[p * 2].val + node_list[p].val + node_list[p * 2 + 1].val;
			node_list[p].offset = 0;

			return p;
		}
	}

	void initialize(vector<long long>& a) {		// initialize with a vector
		size = a.size();
		node_list.assign(size * 4 + 100, { 0, 0 });

		build_tree(0, size - 1, 1, a);		// stored in 1-based, left = p * 2, right = p * 2 + 1s
	}

	long long update(long long pos, long long key) {		// create a new tree by cloning with #root_id and updating position p with new value key
		//long long ans = update_full(1, 0, size - 1, pos, key);
		long long ans = update_range_full(1, 0, size - 1, pos, pos, key);
		return ans;
	}

	long long update_full(long long p, long long left, long long right, long long pos, long long key) {	// p = current node id
		if (left == right) {
			node_list[p].val = key;
			node_list[p].offset = 0;
			return p;		// return node #
		}
		else {
			long long m = (left + right) >> 1;
			long long a, b;
			if (pos <= m) {
				a = update_full(p * 2, left, m, pos, key);
				b = p * 2 + 1;
			}
			else {
				a = p * 2;
				b = update_full(p * 2 + 1, m + 1, right, pos, key);
			}

			// range sum
			node_list[p].val = node_list[a].val + node_list[b].val;
			return p;
		}
	}

	long long update_range(long long l, long long r, long long off) {
		long long ans = update_range_full(1, 0, size - 1, l, r, off);
		return ans;
	}

	long long update_range_full(long long p, long long left, long long right, long long l, long long r, long long off) { // p = current node id
		// cout << "Update " << left << " - " << right << " to " << off << " (p = " << p << ")\n";
		if (left > right) return 0;
		if ((left >= l) && (right <= r)) {
			// fully included in this range. set offset instead of change individual nodes
			if (p == 7) {
				p = 7;
			}
			if (left == right) {
				// leave node
				node_list[p].offset = off;
				node_list[p].val = (off * f[left]) % MOD;
			}
			else {
				node_list[p].offset = off;
				node_list[p].val = ((s[left] - s[right + 1] + MOD) * off) % MOD;
			}
			return node_list[p].val;		// return node #
		}
		else {
			long long m = (left + right) >> 1;
			long long a, b, ans = 0;

			if (node_list[p].offset != 0) {
				update_range_full(p * 2, left, m, left, m, node_list[p].offset);
				update_range_full(p * 2 + 1, m + 1, right, m + 1, right, node_list[p].offset);
			}

			if (m >= l) {
				ans += update_range_full(p * 2, left, m, l, r, off);
			}

			if (m + 1 <= r) {
				ans += update_range_full(p * 2 + 1, m + 1, right, l, r, off);
			}

			// range sum
			node_list[p].val = (node_list[p * 2].val + node_list[p * 2 + 1].val) % MOD;
			node_list[p].offset = 0;		// split

			return ans;
		}
	}

	long long query(long long l, long long r) {  // get range sum between l and r starting from the segment tree indicated by root_id
		if (l > r) {
			return 0;
		}
		return query_full(1, 0, size - 1, l, r, 0);
	}

	inline long long query_full(long long p, long long left, long long right, long long l, long long r, long long off) {	 // left/right = current range, l/r = intended query range; off = carried (uncommitted) offset from parents
		if ((left >= l) && (right <= r)) {
			return node_list[p].val;		// commit offset			
		}
		long long m = (left + right) >> 1, ans = 0;

		if (m >= l) {
			ans += query_full(p * 2, left, m, l, r, off);
		}
		if (m + 1 <= r) {
			ans += query_full(p * 2 + 1, m + 1, right, l, r, off);
		}
		return ans % MOD;
	}
};

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);

	long long n, q, l, r, d, i, j, k, ans; 

	cin >> n >> q;

	f.resize(n + 1);
	s.resize(n + 2);
	f[n] = 1;
	s[n] = 1;
	s[n + 1] = 0;
	for (i = n - 1; i >= 1; i--) {
		f[i] = (f[i + 1] * 10) % MOD;
		s[i] = (s[i + 1] + f[i]) % MOD;
	}

	lazy_segment_tree st;
	st.initialize_empty(n + 1);
	st.update_range(1, n, 1);

	ans = st.query(1, n) % MOD;
	//cout << ans << "\n";

	while (q--) {
		cin >> l >> r >> d;
		st.update_range(l, r, d);

		ans = st.query(1, n) % MOD;
		cout << ans << "\n";
	}

	return 0;
}




Measured memory_bytes: 18764