#include <bits/stdc++.h>
    #include <atcoder/lazysegtree>
    #include <atcoder/modint>
    using namespace std;
    using namespace atcoder;
     
    using mint = modint998244353;
     
    //S is the data type that is stored in the segment tree nodes
    struct S {
    	modint value; //Stores the value represented by all the digits in the segment
    	int digits; //Stores how many digits are in this node
    };
    //For example, a node for "3572" will have value = 3572 and digits = 4
     
    //Op is the operation that merges two segments into one
    S op(S l, S r) {
    	//The new value will be the left value multiplied by 10^(number of digits of the right value) + the right value
    	//The new number of digits is the sum of the number of digits from both segments
    	return S { l.value * modint(10).pow(r.digits) + r.value, l.digits + r.digits };
    }
     
    //e is created such that op(l,e) == l and op(e,r) == r. It should not modify the other value that is not e.
    //The value 0 works since it will add nothing, and digits = 0 doesn't add more digits.
    S e() {
    	return S { 0, 0 };
    }
     
     
    //F contains the attributes of the function that is applied to a segment
    //The actual function is implemented in "mapping"
    struct F {
    	modint digit; //Stores the digit to assign to the segment
    	int t; //Stores the "time" of the operation, later assignments have priority over early ones
    		   //Special case when t == -1, in this case the function will act as the identity function (it will not modify the node)
    };
     
     
    //mapping is the function that is applied to a segment with "apply"
    //This function replaces every digit in the segment
    S mapping(F l, S r) {
    	if (l.t == -1) {
    		//Special case, do not modify the node.
    		return r;
    	}
    	//The value has to be replaced with 'l.digit' repeated 'r.digit' times.
    	//We can do that with the formula 111...1 (n times) = ((10^n)-1)/9 and multiply that by 'l.digit'
    	return S { (modint(10).pow(r.digits) - 1) * modint(9).inv() * l.digit,	r.digits };
    }
     
    //composition is the way to combine two functions that were applied to the same segment.
    //If we have two updates, the one with the latest time will be kept
    F composition(F l, F r) {
    	return F { l.t > r.t ? l : r };
    }
     
    //id should represent a function that does not modify the node.
    //In this case, set t=-1 since it is the special case
    F id() {
    	return F { 0, -1 };
    }
     
    int main() {
    	ios_base::sync_with_stdio(false);
    	cin.tie(0);
     
    	int n;
    	cin >> n;
    	vector<S> arr(n, S { 1, 1 }); //Fill with '1' (also, they have 1 digit)
     
    	lazy_segtree<S, op, e, F, mapping, composition, id> seg(arr);
     
    	int t = 0;
     
    	int q;
    	cin >> q;
    	while (q--) {
    		int x, y;
    		cin >> x >> y;
    		
    		x--;
    		y--;
    		
    		y++; //The right has to be excluded
    		
    		int d;
    		cin >> d;
    		
    		//'apply' applies 'composition' to the segment [x,y) with attributes digit=d and the time is always increased by 1
    		seg.apply(x, y, F { modint(d), t++ });
    		
    		//
    		cout << seg.all_prod().value.val() << "\n";
    	}
     
    }

Measured memory_bytes: 10784