#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>

using namespace std;

typedef long long ll;

//このnamespace内のMODという値を、問題の要求に応じて変更すること
namespace Mint {

	//このMODという値を、問題の要求に応じて変更すること
	const ll MOD = 998244353;

	template<ll Mod>
	struct Modint {
		ll val = 0;

		//コンストラクタ long long, 空, Modintを受け取れる
		Modint() = default;
		Modint(const Modint&) = default;
		Modint(ll _x) {
			val = _x >= 0 ? _x % Mod : ((_x % Mod) + Mod) % Mod;
		}

		//繰り返し二乗法、逆元　基本的に外部からいじるのやめたほうがよさそう。
		ll modpow(ll a, ll b) const {
			ll ret = 1, kakeru = a;
			while (b > 0) {
				if (b & 1)ret *= kakeru, ret %= Mod;
				kakeru *= kakeru, kakeru %= Mod;
				b >>= 1;
			}
			return ret;
		}
		Modint inv() const {
			return modpow((*this).val, MOD - 2);
		}

		//代入演算子　Modintとlong longの2通りある
		Modint operator= (const Modint& p) {
			val = p.val;
			return (*this);
		}

		//二項演算＋代入演算子　二項演算子、同値演算子はクラス外で定義する
		Modint& operator+= (const Modint& p) {
			val += p.val;
			if (val >= Mod)val -= Mod;
			return (*this);
		}
		Modint& operator-= (const Modint& p) {
			val -= p.val;
			if (val < 0)val += Mod;
			return (*this);
		}
		Modint& operator*= (const Modint& p) {
			val *= p.val;
			val %= Mod;
			return (*this);
		}
		Modint& operator/= (const Modint& p) {
			//なんか,p.inv()を使うとthisのポインターが変換できませんって出る
			//Modint tmp(p.inv());
			Modint tmp(modpow(p.val, MOD - 2));
			(*this) *= tmp;
			return (*this);
		}
	};

	//加算
	const Modint<MOD> operator+ (const Modint<MOD>& l, const Modint<MOD>& r) {
		Modint<MOD> tmp = l;
		tmp += r;
		return tmp;
	}
	const Modint<MOD> operator+ (const Modint<MOD>& l, const ll r) {
		Modint<MOD> tmp = l;
		tmp += Modint<MOD>(r);
		return tmp;
	}
	const Modint<MOD> operator+ (const ll l, const Modint<MOD>& r) {
		Modint<MOD> tmp = l;
		tmp += r;
		return tmp;
	}
	//減算
	const Modint<MOD> operator- (const Modint<MOD>& l, const Modint<MOD>& r) {
		Modint<MOD> tmp = l;
		tmp -= r;
		return tmp;
	}
	const Modint<MOD> operator- (const Modint<MOD>& l, const ll r) {
		Modint<MOD> tmp = l;
		tmp -= Modint<MOD>(r);
		return tmp;
	}
	const Modint<MOD> operator- (const ll l, const Modint<MOD>& r) {
		Modint<MOD> tmp = l;
		tmp -= r;
		return tmp;
	}
	//乗算
	const Modint<MOD> operator* (const Modint<MOD>& l, const Modint<MOD>& r) {
		Modint<MOD> tmp = l;
		tmp *= r;
		return tmp;
	}
	const Modint<MOD> operator* (const Modint<MOD>& l, const ll r) {
		Modint<MOD> tmp = l;
		tmp *= Modint<MOD>(r);
		return tmp;
	}
	const Modint<MOD> operator* (const ll l, const Modint<MOD>& r) {
		Modint<MOD> tmp = l;
		tmp *= r;
		return tmp;
	}
	//除算
	const Modint<MOD> operator/ (const Modint<MOD>& l, const Modint<MOD>& r) {
		Modint<MOD> tmp = l;
		tmp /= r;
		return tmp;
	}
	const Modint<MOD> operator/ (const Modint<MOD>& l, const ll r) {
		Modint<MOD> tmp = l;
		tmp /= Modint<MOD>(r);
		return tmp;
	}
	const Modint<MOD> operator/ (const ll l, const Modint<MOD>& r) {
		Modint<MOD> tmp = l;
		tmp /= r;
		return tmp;
	}
	//同値演算子
	const bool operator== (const Modint<MOD>& l, const Modint<MOD>& r) {
		return l.val == r.val;
	}
	const bool operator== (const Modint<MOD>& l, const ll r) {
		return l.val == r;
	}
	const bool operator== (const ll l, const Modint<MOD>& r) {
		return l == r.val;
	}
	const bool operator!= (const Modint<MOD>& l, const Modint<MOD>& r) {
		return !(l.val == r.val);
	}
	const bool operator!= (const Modint<MOD>& l, const ll r) {
		return !(l.val == r);
	}
	const bool operator!= (const ll l, const Modint<MOD>& r) {
		return !(l == r.val);
	}
	//istream ostream での入出力サポート
	std::ostream& operator<<(std::ostream& stream, const Modint<MOD>& p) {
		stream << p.val;
		return stream;
	}
	std::istream& operator>>(std::istream& stream, Modint<MOD>& p) {
		stream >> p.val;
		return stream;
	}
	//使う用の繰り返し二乗法 bはlong long に注意
	Modint<MOD> modpow(const Modint<MOD> a, ll b) {
		ll ret = 1, kakeru = a.val;
		while (b > 0) {
			if (b & 1)ret *= kakeru, ret %= MOD;
			kakeru *= kakeru, kakeru %= MOD;
			b >>= 1;
		}
		Modint<MOD> tmpret(ret);
		return tmpret;
	}
}

using namespace Mint;
using mint = Mint::Modint<MOD>;


//遅延セグ木
/*
Monoid1 -> データの型
Monoid2 -> 遅延配列に入れる、Monoid1型のデータに作用する用のデータの型 (RSQならば、Monoid1, 2共にint型)
operation1 -> Monoid1(Monoid1, Monoid1) 保存されてるデータ同士の統合の演算子
operation2 -> Monoid2(Monoid2, Monoid2) 遅延配列に入ってるデータ同士の統合の演算子
operation3 -> Monoid1(Monoid1, Monoid2) 保存されてるデータに、遅延配列を作用させる演算子
operation4 -> Monoid2(Monoid2, int) 遅延配列の区間の長さに対応して、もともと割り当てられた遅延配列の中身を演算するときに使う演算子
RSQならば、上記の3つはすべて加算(と乗算)でまかなえる。operation4は、RSQならばカバーしてる区間倍することに相当する。
*/
template <class Monoid1, class Monoid2> struct LazySegTree {
	int N;
	vector<Monoid1> node;
	vector<Monoid2> lazy;
	std::function<Monoid1(Monoid1, Monoid1)> operation1;
	std::function<Monoid2(Monoid2, Monoid2)> operation2;
	std::function<Monoid1(Monoid1, Monoid2, int, int)> operation3;
	std::function<Monoid2(Monoid2, int)> operation4;
	Monoid1 unit1;//Monoid1の単位元
	Monoid2 unit2;//Monoid2の単位元

	LazySegTree(vector<Monoid1>& data,
		std::function<Monoid1(Monoid1, Monoid1)> op1,
		std::function<Monoid2(Monoid2, Monoid2)> op2,
		std::function<Monoid1(Monoid1, Monoid2, int, int)> op3,
		std::function<Monoid2(Monoid2, int)> op4,
		Monoid1 U1, Monoid2 U2) {
		//データ　演算1~4　単位元1~2
		operation1 = op1;
		operation2 = op2;
		operation3 = op3;
		operation4 = op4;
		unit1 = U1;
		unit2 = U2;
		int size = data.size();
		N = 1;
		while (N < size)N <<= 1;
		node.resize(2 * N - 1, unit1);
		lazy.resize(2 * N - 1, unit2);

		for (int i = 0; i < size; i++) {
			node[i + N - 1] = data[i];
		}

		for (int i = N - 2; i >= 0; i--) {
			node[i] = operation1(node[i * 2 + 1], node[i * 2 + 2]);
		}
	}

	//番号kのデータを伝播させる。
	void eval(int k, int l, int r) {
		if (lazy[k] == unit2)return;
		node[k] = operation3(node[k], operation4(lazy[k], r - l), r - l, r);
		if (r - l > 1) {
			//子を持ってる
			lazy[2 * k + 1] = operation2(lazy[2 * k + 1], lazy[k]);
			lazy[2 * k + 2] = operation2(lazy[2 * k + 2], lazy[k]);
		}
		lazy[k] = unit2;
	}

	void update(Monoid2 val, int idx, int a, int b, int l = 0, int r = -1) {
		//[a, b)
		if (r < 0)r = N;
		eval(idx, l, r);
		//範囲外なら何もしない
		if (b <= l || r <= a)return;
		if (a <= l && r <= b) {
			//完全に被覆してるのなら、遅延配列を更新して評価。
			lazy[idx] = operation2(lazy[idx], val);
			eval(idx, l, r);
			return;
		}
		update(val, idx * 2 + 1, a, b, l, (l + r) / 2);
		update(val, idx * 2 + 2, a, b, (l + r) / 2, r);
		node[idx] = operation1(node[idx * 2 + 1], node[idx * 2 + 2]);
	}

	void update(Monoid2 val, int a, int b) {
		//[a, b)
		update(val, 0, a, b, 0, -1);
	}

	Monoid1 getval(int a, int b, int idx, int l, int r = -1) {
		eval(idx, l, r);
		//[a, b)を求める。　使い方はgetval(a, b, );
		if (r == -1)r = N;
		if (b <= l || r <= a)return unit1;
		if (a <= l && r <= b)return node[idx];
		Monoid1 left = getval(a, b, idx * 2 + 1, l, (l + r) / 2);
		Monoid1 right = getval(a, b, idx * 2 + 2, (l + r) / 2, r);
		return operation1(left, right);
	}

	Monoid1 getval(int a, int b) {
		//ラッパー。
		return getval(a, b, 0, 0, -1);
	}
};

int N, Q;

/*
Monoid1 -> データの型
Monoid2 -> 遅延配列に入れる、Monoid1型のデータに作用する用のデータの型 (RSQならば、Monoid1, 2共にint型)
operation1 -> Monoid1(Monoid1, Monoid1) 保存されてるデータ同士の統合の演算子
operation2 -> Monoid2(Monoid2, Monoid2) 遅延配列に入ってるデータ同士の統合の演算子
operation3 -> Monoid1(Monoid1, Monoid2) 保存されてるデータに、遅延配列を作用させる演算子
operation4 -> Monoid2(Monoid2, int) 遅延配列の区間の長さに対応して、もともと割り当てられた遅延配列の中身を演算するときに使う演算子
RSQならば、上記の3つはすべて加算(と乗算)でまかなえる。operation4は、RSQならばカバーしてる区間倍することに相当する。
*/

vector<mint> pow10, conti1;

int main() {
	cin >> N >> Q;
	vector<mint> lst_ar(N, mint(0));
	pow10.resize(N);
	conti1.resize(N + 1);
	lst_ar[N - 1] = 1;
	pow10[0] = 1;
	conti1[1] = 1;
	for (int i = N - 2; i >= 0; i--) {
		lst_ar[i] = lst_ar[i + 1] * 10;
		pow10[N - 1 - i] = pow10[N - 2 - i] * 10;
	}
	for (int i = 2; i <= N; i++)
		conti1[i] = conti1[i - 1] * 10 + 1;

	LazySegTree<mint, int> lst(lst_ar,
		[](mint a, mint b) {return a + b; },
		[](int a, int b) {return b; },
		[&](mint a, int b, int range, int r){
			return conti1[range] * b * pow10[N - r];
		},
		[](int a, int times) {return a; },
		0, 0
		);
	while (Q--) {
		int L, R, D;//[L, R]
		cin >> L >> R >> D;
		L--, R--;
		lst.update(D, L, R + 1);
		cout << lst.getval(0, N) << endl;
	}
	
	return 0;
}

Measured memory_bytes: 14064