#include <vector>
#include <functional>

template<class T, class E, class F, class G, class H>
struct lazy_segment_tree {
	using value_type = T;
	using operand_type = E;
	using size_type = std::size_t;
private :
	class node_type {
	public :
		value_type value;
		operand_type operand;
		node_type () = default;
		node_type (const value_type &value, const operand_type &operand)
		noexcept : value(value), operand(operand) { }
	};

	size_type n;
	const F f;
	const G g;
	const H h;
	const value_type value_identity;
	const operand_type operand_identity;
	std::vector<node_type> node;

	value_type reflect (const size_type &i) noexcept {
		if (node[i].operand == operand_identity) return node[i].value;
		return g(node[i].value, node[i].operand);
	}

	void merge (const size_type &i) noexcept {
		node[i].value = f(reflect(i << 1 | 0), reflect(i << 1 | 1));
	}

	void add (const size_type &i, const operand_type &operand) noexcept {
		node[i].operand = h(node[i].operand, operand);
	}

	void propagate (size_type i) noexcept {
		if (node[i].operand == operand_identity) return;
		add(i << 1 | 0, node[i].operand);
		add(i << 1 | 1, node[i].operand);
		node[i].value = reflect(i);
		node[i].operand = operand_identity;
	}

	void topdown_update (size_type i) noexcept {
		if (i == 0) return;
		for (size_type h = 32 - __builtin_clz(i); h; h--) {
			propagate(i >> h);
		}
	}

	void bottomup_update (size_type i) noexcept {
		if (i == 0) return;
		for (i >>= 1; i > 0; i >>= 1) {
			merge(i);
		}
	}

public :
	lazy_segment_tree (const value_type &value_identity, const operand_type &operand_identity, const F &f, const G &g, const H &h)
	noexcept : value_identity(value_identity), operand_identity(operand_identity), f(f), g(g), h(h) { }

	void assign (size_type _size) noexcept {
		n = _size;
		node.assign(_size << 1, node_type(value_identity, operand_identity));
	}

	void assign(size_type _size, const value_type &value) noexcept {
		assign(_size);
		for (size_type i = n; i < node.size(); i++) node[i].value = value;
		for (size_type i = n; i --> 0;) merge(i);
	}

	template<class InputIterator>
	void assign (InputIterator first, InputIterator last) noexcept {
		assign(last - first);
		for (size_type i = n; first != last; first++, i++) node[i].value = (*first);
		for (size_type i = n; i --> 0;) merge(i);
	}

	void update (size_type first, size_type last, const operand_type &operand) noexcept {
		const size_type _first = first + n, _last = last + n - 1;
		topdown_update(_first);
		topdown_update(_last);
		for (first += n, last += n; first != last; first >>= 1, last >>= 1) {
			if (first & 1) add(first++, operand);
			if (last  & 1) add(--last,  operand);
		}
		bottomup_update(_first);
		bottomup_update(_last);
	}

	value_type fold (size_type first, size_type last) noexcept {
		topdown_update(first + n);
		topdown_update(last + n - 1);
		value_type left = value_identity, right = value_identity;
		for (first += n, last += n; first != last; first >>= 1, last >>= 1) {
			if (first & 1) left = f(left, reflect(first++));
			if (last & 1) right = f(reflect(--last), right);
		}
		return f(left, right);
	}

};

template<class InputIterator, class T, class E, class F, class G, class H>
lazy_segment_tree<T, E, F, G, H> make_lazysegtree (InputIterator first, InputIterator last, const T &value_identity, const E &operand_identity, const F &f, const G &g, const H &h) {
	lazy_segment_tree<T, E, F, G, H> tree(value_identity, operand_identity, f, g, h);
	tree.assign(first, last);
	return std::move(tree);
}

template<class T, class E, class F, class G, class H>
lazy_segment_tree<T, E, F, G, H> make_lazysegtree (const T &value_identity, const E &operand_identity, const F &f, const G &g, const H &h) {
	return lazy_segment_tree<T, E, F, G, H>(value_identity, operand_identity, f, g, h);
}


#include <iostream>
#include <cstdint>
#include <cassert>

template<std::int_fast32_t Modulus>
struct modint {
	using value_type = std::int_fast32_t;

	static constexpr std::int_fast32_t mod = Modulus;
	static_assert(mod > 1, "Modulus must be greater than 1");

private :
	value_type x;

	template<class Tp>
	static constexpr value_type normalize (Tp value) noexcept {
		if (value < 0) {
			value = (-value) % mod;
			return (value ? value : mod - value);
		}
		return (value % mod);
	}

public :
	explicit constexpr modint () noexcept : x(0) { }

	template<class Tp>
	constexpr modint (Tp value) noexcept : x(normalize(value)) { }

	constexpr modint operator+ () const noexcept {
		return modint(*this);
	}

	constexpr modint operator- () noexcept {
		if (x != 0) x = mod - x;
		return modint(*this);
	}

	constexpr modint &operator+= (const modint &p) noexcept {
		if ((x += p.x) >= mod) x -= mod;
		return (*this);
	}

	constexpr modint &operator-= (const modint &p) noexcept {
		if ((x += mod - p.x) >= mod) x -= mod;
		return (*this);
	}

	constexpr modint &operator*= (const modint &p) noexcept {
		x = (int)(1LL * x * p.x % mod);
		return (*this);
	}

	constexpr modint &operator/= (const modint &p) noexcept {
		return ((*this) *= p.inverse());
	}

	constexpr modint operator+ (const modint &p) const noexcept {
		return (modint(*this) += p);
	}

	constexpr modint operator- (const modint &p) const noexcept {
		return (modint(*this) -= p);
	}

	constexpr modint operator* (const modint &p) const noexcept {
		return (modint(*this) *= p);
	}

	constexpr modint operator/ (const modint &p) const noexcept {
		return (modint(*this) /= p);
	}

	constexpr bool operator== (const modint &p) const noexcept {
		return (x == p.x);
	}

	constexpr bool operator!= (const modint &p) const noexcept {
		return (x != p.x);
	}

	template<class Tp>
	constexpr modint pow (Tp exp) const noexcept {
		modint value(1), buff(x);
		while (exp) {
			if (exp & 1) value *= buff;
			buff *= buff;
			exp >>= 1;
		}
		return value;
	}

	constexpr modint inverse () const noexcept {
		return pow(mod - 2);
	}

	friend std::ostream &operator<< (std::ostream &os, const modint &p) {
		return (os << p.x);
	}

	friend std::istream &operator>> (std::istream &is, modint &p) {
		long long v;
		is >> v;
		p = modint(v);
		return (is);
	}

};


#include <bits/stdc++.h>

constexpr std::int_fast32_t MOD = 998244353;
constexpr std::size_t MAX = 200200;

using mint = modint<MOD>;

using node = std::pair<std::pair<mint, int>, int>;

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	
	std::vector<std::vector<mint>> v(10, std::vector<mint>(MAX, 0));
	for (int i = 0; i < 10; i++) {
		v[i][0] = 0;
		for (int j = 0; j + 1 < MAX; j++) {
			v[i][j + 1] = v[i][j] * 10 + i;
		}
	}
	
	auto f = [&](node l, node r) {
		if (l.first.second < r.first.second) std::swap(l, r);
		auto [p1, len1] = l; auto [k1, s] = p1;
		auto [p2, len2] = r; auto [k2, t] = p2;
		mint k = k1 * mint(10).pow(s - t) + k2;
		auto p = std::make_pair(k, t);
		return node(p, len1 + len2);
	};
	auto g = [&](node l, int d) {
		mint value = v[d][l.second];
		l.first.first = value;
		return l;
	};
	auto h = [](int a, int b) { return (b == 0 ? a : b); };
	
	int n, q;
	std::cin >> n >> q;
	
	std::vector<node> a(n);
	for (int i = 0; i < n; i++) {
		a[i].first.first = 1;
		a[i].first.second = n - 1 - i;
		a[i].second = 1;
	}
	const node id(std::make_pair(0, 0), 0);
	
	auto seg = make_lazysegtree(id, 0, f, g, h);
	seg.assign(a.begin(), a.end());
	
	for (int i = 0; i < q; i++) {
		std::size_t l, r;
		int d;
		std::cin >> l >> r >> d;
		l--; r--;
		seg.update(l, r + 1, d);
		
		auto value = seg.fold(0, n);
		mint ans = value.first.first * mint(10).pow(value.first.second);
		
		std::cout << ans << '\n';
	}
	
	return 0;
}

Measured memory_bytes: 36200