Problem:
Score : 500 points

Problem Statement

You have a string S of length N.
Initially, all characters in S are 1s.

You will perform queries Q times.
In the i-th query, you are given two integers L_i, R_i and a character D_i (which is a digit).
Then, you must replace all characters from the L_i-th to the R_i-th (inclusive) with D_i.

After each query, read the string S as a decimal integer, and print its value modulo 998,244,353.

Constraints

1 \leq N, Q \leq 200,000

1 \leq L_i \leq R_i \leq N

1 \leq D_i \leq 9

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N Q
L_1 R_1 D_1
:
L_Q R_Q D_Q

Output

Print Q lines.
In the i-th line print the value of S after the i-th query, modulo 998,244,353.

Sample Input 1

8 5
3 6 2
1 4 7
3 8 3
2 2 2
4 5 1

Sample Output 1

11222211
77772211
77333333
72333333
72311333

Sample Input 2

200000 1
123 456 7

Sample Output 2

641437905

Don't forget to take the modulo.

Solution in C# (.NET Core 3.1.201) (judge verdict: Time Limit Exceeded)

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Diagnostics;
using System.Runtime.Intrinsics.X86;
using System.Runtime.CompilerServices;
using FertiLib.Contest.D.AtCoder;


namespace FertiLib.Contest.D
{
	static class Program
	{
		public static void Solve(Scanner cin)
		{
			var pow = new ModInt[200005];
			ModInt t = 1;
			for (int i = 0; i < 200005; i++)
			{
				pow[i] = t;
				t *= 10;
			}
			Op.pow = pow;
			var (n, q) = cin.ReadValue<int, int>();
			var lseg = new LazySegtree<(ModInt v, int count), ModInt, Op>(Enumerable.Repeat((new ModInt(1), 1), n).ToArray());
			for (int i = 0; i < q; i++)
			{
				var (l, r, d) = cin.ReadValue<int, int, int>();
				lseg.Apply(n - r, n - l + 1, d);
				Console.WriteLine(lseg.AllProd.v);
			}
		}

		public static void Main(string[] args)
		{
			var sw = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false };
			Console.SetOut(sw);
			var cin = new Scanner();
			Solve(cin);
			Console.Out.Flush();
		}

		public static void YESNO(bool condition) => Console.WriteLine(condition ? "YES" : "NO");
		public static void YesNo(bool condition) => Console.WriteLine(condition ? "Yes" : "No");
		public static void yesno(bool condition) => Console.WriteLine(condition ? "yes" : "no");

		public static bool Chmax<T>(ref T a, T b) where T : IComparable<T>
		{
			if (a.CompareTo(b) >= 0) return false;
			a = b;
			return true;
		}
		public static bool Chmin<T>(ref T a, T b) where T : IComparable<T>
		{
			if (a.CompareTo(b) <= 0) return false;
			a = b;
			return true;
		}
	}

	public struct Op : IMonoidFuncOperator<(ModInt v, int count), ModInt>
	{
		public static ModInt[] pow;
		static (ModInt v, int count) Merge((ModInt v, int count) x, (ModInt v, int count) y)
		{
			return (x.v + y.v * ModInt.Pow(10, x.count), x.count + y.count);
		}
		(ModInt v, int count) IMonoidFuncOperator<(ModInt v, int count), ModInt>.Identity => (new ModInt(0), 0);
		ModInt IMonoidFuncOperator<(ModInt v, int count), ModInt>.FIdentity => new ModInt(11);
		(ModInt v, int count) IMonoidFuncOperator<(ModInt v, int count), ModInt>.Operate((ModInt v, int count) x, (ModInt v, int count) y) => Merge(x, y);
		(ModInt v, int count) IMonoidFuncOperator<(ModInt v, int count), ModInt>.Mapping(ModInt f, (ModInt v, int count) x)
		{
			if (f != 11) x.v = f * (pow[x.count] - 1) / 9;
			return x;
		}
		ModInt IMonoidFuncOperator<(ModInt v, int count), ModInt>.Composition(ModInt x, ModInt y) => x == 11 ? y : x;
	}

	public class ModPow
	{
		public List<ModInt> powlist;

		public ModPow(int n)
		{
			powlist = new List<ModInt>();
			ModInt t = 1;
			for (int i = 0; i < n + 1; i++)
			{
				powlist.Add(t);
				t *= 10;
			}
		}
	}

	/// <summary>
	/// Fast moduler by barrett reduction
	/// <seealso href="https://en.wikipedia.org/wiki/Barrett_reduction"/>
	/// </summary>
	public class Barrett
	{
		public uint Mod { get; private set; }
		private ulong IM;
		public Barrett(uint m)
		{
			Mod = m;
			IM = unchecked((ulong)-1) / m + 1;
		}

		/// <summary>
		/// <paramref name="a"/> * <paramref name="b"/> mod m
		/// </summary>
		public uint Mul(uint a, uint b)
		{
			ulong z = a;
			z *= b;
			if (!Bmi2.X64.IsSupported) return (uint)(z % Mod);
			var x = Bmi2.X64.MultiplyNoFlags(z, IM);
			var v = unchecked((uint)(z - x * Mod));
			if (Mod <= v) v += Mod;
			return v;
		}
	}

	public static partial class InternalMath
	{
		public static long SafeMod(long x, long m)
		{
			x %= m;
			if (x < 0) x += m;
			return x;
		}
	}

	public struct ModInt : IComparable<ModInt>, IEquatable<ModInt>
	{
		public static long MOD = 998244353;

		private readonly long num;

		public ModInt(long n) { num = n; }

		public override string ToString() => num.ToString();

		public static ModInt operator +(ModInt l, ModInt r)
		{
			long x = l.num + r.num;
			if (x >= MOD) x -= MOD;
			return new ModInt(x);
		}
		public static ModInt operator -(ModInt l, ModInt r)
		{
			long x = l.num - r.num;
			if (x < 0) x += MOD;
			return new ModInt(x);
		}
		public static ModInt operator *(ModInt l, ModInt r) => new ModInt((l.num * r.num) % MOD);
		public static ModInt operator /(ModInt l, ModInt r) => l * r.Inverse();

		public static ModInt operator ++(ModInt x)
		{
			var tmp = x + new ModInt(1);
			return tmp;
		}
		public static ModInt operator --(ModInt x)
		{
			var tmp = x - new ModInt(1);
			return tmp;
		}

		public static bool operator >(ModInt l, ModInt r) => l.CompareTo(r) > 0;
		public static bool operator <(ModInt l, ModInt r) => l.CompareTo(r) < 0;
		public static bool operator >=(ModInt l, ModInt r) => l.CompareTo(r) >= 0;
		public static bool operator <=(ModInt l, ModInt r) => l.CompareTo(r) <= 0;
		public static bool operator ==(ModInt l, ModInt r) => l.Equals(r);
		public static bool operator !=(ModInt l, ModInt r) => !l.Equals(r);

		public static implicit operator long(ModInt x) => x.num;
		public static implicit operator ModInt(long n)
		{
			n %= MOD;
			if (n < 0) n += MOD;
			return new ModInt(n);
		}

		public ModInt Inverse() => Inverse(this, MOD);
		public static ModInt Inverse(ModInt x) => Inverse(x.num, MOD);
		public static long Inverse(long x, long m)
		{
			if (x % m == 0) throw new DivideByZeroException();
			long a = x, b = m, u = 1, v = 0;
			while (b > 0)
			{
				long t = a / b;

				a -= t * b;
				long p = a; a = b; b = p;   // swap(a, b);

				u -= t * v;
				p = u; u = v; v = p;        // swap(u, v);
			}
			u %= m;
			if (u < 0) u += m;
			return u;
		}

		public static ModInt Pow(long x, long n) => Pow(x, n, (int)MOD);

		public static long Pow(long x, long n, int m)
		{
			Debug.Assert(0 <= n && 1 <= m);
			if (m == 1) return 0;
			Barrett barrett = new Barrett((uint)m);
			uint r = 1, y = (uint)InternalMath.SafeMod(x, m);
			while (0 < n)
			{
				if ((n & 1) != 0) r = barrett.Mul(r, y);
				y = barrett.Mul(y, y);
				n >>= 1;
			}
			return r;
		}

		public int CompareTo(ModInt x)
		{
			if (num == x.num) return 0;
			if (num > x.num) return 1;
			return -1;
		}

		public bool Equals(ModInt x) => num == x.num;

		public override bool Equals(object obj) => obj is ModInt m && num == m.num;

		public override int GetHashCode()
		{
			return HashCode.Combine(num);
		}

	}


	namespace AtCoder
	{
		/// <summary>
		/// モノイドを定義するインターフェイスです。
		/// </summary>
		/// <typeparam name="T">操作を行う型。</typeparam>
		/// <typeparam name="F">写像の型。</typeparam>
		public interface IMonoidFuncOperator<T, F>
		{
			/// <summary>
			/// Operate(x, <paramref name="Identity"/>) = x を満たす単位元。
			/// </summary>
			T Identity { get; }
			/// <summary>
			/// Mapping(<paramref name="FIdentity"/>, x) = x を満たす恒等写像。
			/// </summary>
			F FIdentity { get; }
			/// <summary>
			/// 結合律 Operate(Operate(a, b), c) = Operate(a, Operate(b, c)) を満たす写像。
			/// </summary>
			T Operate(T x, T y);
			/// <summary>
			/// 写像　<paramref name="f"/> を <paramref name="x"/> に作用させる関数。
			/// </summary>
			T Mapping(F f, T x);
			/// <summary>
			/// 写像　<paramref name="f"/>, <paramref name="g"/> を 合成した写像 <paramref name="f"/>∘<paramref name="g"/>。
			/// </summary>
			F Composition(F f, F g);
		}

		/// <summary>
		/// 長さ N の配列に対し、
		/// <list type="bullet">
		/// <item>
		/// <description>区間の要素に一括で <typeparamref name="TOp"/> の要素 f を作用 ( x=f(x) )</description>
		/// </item>
		/// <item>
		/// <description>区間の要素の総積の取得</description>
		/// </item>
		/// </list>
		/// <para>を O(log N) で求めることが出来るデータ構造です。</para>
		/// </summary>
		[DebuggerTypeProxy(typeof(LazySegtree<,,>.DebugView))]
		public class LazySegtree<TValue, F, TOp> where TOp : struct, IMonoidFuncOperator<TValue, F>
		{
			private static readonly TOp op = default;

			/// <summary>
			/// 数列 a の長さ n を返します。
			/// </summary>
			public int Length { get; }

			private readonly int log;
			private readonly int size;
			private readonly TValue[] d;
			private readonly F[] lz;


			/// <summary>
			/// 長さ <paramref name="n"/> の数列 a　を持つ <see cref="LazySegtree{TValue, TOp}"/> クラスの新しいインスタンスを作ります。初期値は <see cref="TOp.Identity"/> です。
			/// </summary>
			/// <remarks>
			/// <para>制約: 0≤<paramref name="n"/>≤10^8</para>
			/// <para>計算量: O(<paramref name="n"/>)</para>
			/// </remarks>
			/// <param name="n">配列の長さ</param>
			public LazySegtree(int n)
			{
				AssertMonoid(op.Identity);
				AssertFIdentity(op.Identity);
				AssertF(op.FIdentity, op.Identity, op.Identity);
				Length = n;
				log = InternalMath.CeilPow2(n);
				size = 1 << log;
				d = new TValue[2 * size];
				lz = new F[size];
				Array.Fill(d, op.Identity);
				Array.Fill(lz, op.FIdentity);
			}

			/// <summary>
			/// 長さ n=<paramref name="v"/>.Length の数列 a　を持つ <see cref="LazySegtree{TValue, TOp}"/> クラスの新しいインスタンスを作ります。初期値は <paramref name="v"/> です。
			/// </summary>
			/// <remarks>
			/// <para>制約: 0≤<paramref name="n"/>≤10^8</para>
			/// <para>計算量: O(<paramref name="n"/>)</para>
			/// </remarks>
			/// <param name="n">配列の長さ</param>
			public LazySegtree(TValue[] v) : this(v.Length)
			{
				for (int i = 0; i < v.Length; i++) d[size + i] = v[i];
				for (int i = size - 1; i >= 1; i--)
				{
					Update(i);
				}
			}

			[MethodImpl(MethodImplOptions.AggressiveInlining)]
			private void Update(int k) => d[k] = op.Operate(d[2 * k], d[2 * k + 1]);

			[MethodImpl(MethodImplOptions.AggressiveInlining)]
			private void AllApply(int k, F f)
			{
				AssertF(f, op.Identity, op.Identity);
				AssertMonoid(d[k]);
				AssertFIdentity(d[k]);
				AssertF(f, d[k], d[k]);
				d[k] = op.Mapping(f, d[k]);
				if (k < size) lz[k] = op.Composition(f, lz[k]);
			}
			[MethodImpl(MethodImplOptions.AggressiveInlining)]
			private void Push(int k)
			{
				AllApply(2 * k, lz[k]);
				AllApply(2 * k + 1, lz[k]);
				lz[k] = op.FIdentity;
			}

			/// <summary>
			/// a[<paramref name="p"/>] を返します。
			/// </summary>
			/// <remarks>
			/// <para>制約: 0≤<paramref name="p"/>&lt;n</para>
			/// <para>計算量: O(log n)</para>
			/// </remarks>
			/// <returns></returns>
			public TValue this[int p]
			{
				[MethodImpl(MethodImplOptions.AggressiveInlining)]
				set
				{
					Debug.Assert((uint)p < Length);
					p += size;
					for (int i = log; i >= 1; i--) Push(p >> i);
					d[p] = value;
					for (int i = 1; i <= log; i++) Update(p >> i);
				}
				[MethodImpl(MethodImplOptions.AggressiveInlining)]
				get
				{
					Debug.Assert((uint)p < Length);
					p += size;
					for (int i = log; i >= 1; i--) Push(p >> i);
					return d[p];
				}
			}

			/// <summary>
			/// <see cref="TOp.Operate"/>(a[<paramref name="l"/>], ..., a[<paramref name="r"/> - 1]) を返します。<paramref name="l"/> = <paramref name="r"/> のときは　<see cref="TOp.Identity"/> を返します。
			/// </summary>
			/// <remarks>
			/// <para>制約: 0≤<paramref name="l"/>≤<paramref name="r"/>≤n</para>
			/// <para>計算量: O(log n)</para>
			/// </remarks>
			/// <returns><see cref="TOp.Operate"/>(a[<paramref name="l"/>], ..., a[<paramref name="r"/> - 1])</returns>
			[MethodImpl(MethodImplOptions.AggressiveInlining)]
			public TValue Prod(int l, int r)
			{
				Debug.Assert(0 <= l && l <= r && r <= Length);
				if (l == r) return op.Identity;

				l += size;
				r += size;

				for (int i = log; i >= 1; i--)
				{
					if (((l >> i) << i) != l) Push(l >> i);
					if (((r >> i) << i) != r) Push(r >> i);
				}

				TValue sml = op.Identity, smr = op.Identity;
				while (l < r)
				{
					if ((l & 1) != 0) sml = op.Operate(sml, d[l++]);
					if ((r & 1) != 0) smr = op.Operate(d[--r], smr);
					l >>= 1;
					r >>= 1;
				}

				return op.Operate(sml, smr);
			}

			/// <summary>
			/// <see cref="TOp.Operate"/>(a[0], ..., a[n - 1]) を返します。n = 0 のときは　<see cref="TOp.Identity"/> を返します。
			/// </summary>
			/// <remarks>
			/// <para>計算量: O(1)</para>
			/// </remarks>
			/// <returns><see cref="TOp.Operate"/>(a[0], ..., a[n - 1])</returns>
			public TValue AllProd => d[1];

			/// <summary>
			/// a[<paramref name="p"/>] = <see cref="TOp.Mapping"/>(<paramref name="f"/>, a[<paramref name="p"/>])
			/// </summary>
			/// <remarks>
			/// <para>制約: 0≤<paramref name="p"/>≤n</para>
			/// <para>計算量: O(log n)</para>
			/// </remarks>
			public void Apply(int p, F f)
			{
				Debug.Assert((uint)p < Length);
				p += size;
				for (int i = log; i >= 1; i--) Push(p >> i);
				d[p] = op.Mapping(f, d[p]);
				for (int i = 1; i <= log; i++) Update(p >> i);
			}

			/// <summary>
			/// i = <paramref name="l"/>..<paramref name="r"/> について a[i] = <see cref="TOp.Mapping"/>(<paramref name="f"/>, a[<paramref name="p"/>])
			/// </summary>
			/// <remarks>
			/// <para>制約: 0≤<paramref name="l"/>≤<paramref name="r"/>≤n</para>
			/// <para>計算量: O(log n)</para>
			/// </remarks>
			public void Apply(int l, int r, F f)
			{
				Debug.Assert(0 <= l && l <= r && r <= Length);
				if (l == r) return;

				l += size;
				r += size;

				for (int i = log; i >= 1; i--)
				{
					if (((l >> i) << i) != l) Push(l >> i);
					if (((r >> i) << i) != r) Push((r - 1) >> i);
				}

				{
					int l2 = l, r2 = r;
					while (l < r)
					{
						if ((l & 1) != 0) AllApply(l++, f);
						if ((r & 1) != 0) AllApply(--r, f);
						l >>= 1;
						r >>= 1;
					}
					l = l2;
					r = r2;
				}

				for (int i = 1; i <= log; i++)
				{
					if (((l >> i) << i) != l) Update(l >> i);
					if (((r >> i) << i) != r) Update((r - 1) >> i);
				}
			}



			/// <summary>
			/// 以下の条件を両方満たす r を(いずれか一つ)返します。
			/// <list type="bullet">
			/// <item>
			/// <description>r = <paramref name="l"/> もしくは <paramref name="g"/>(op(a[<paramref name="l"/>], a[<paramref name="l"/> + 1], ..., a[r - 1])) = true</description>
			/// </item>
			/// <item>
			/// <description>r = n もしくは <paramref name="g"/>(op(a[<paramref name="l"/>], a[<paramref name="l"/> + 1], ..., a[r])) = false</description>
			/// </item>
			/// </list>
			/// <para><paramref name="g"/> が単調だとすれば、<paramref name="g"/>(op(a[<paramref name="l"/>], a[<paramref name="l"/> + 1], ..., a[r - 1])) = true となる最大の r、と解釈することが可能です。</para>
			/// </summary>
			/// <remarks>
			/// 制約
			/// <list type="bullet">
			/// <item>
			/// <description><paramref name="g"/> を同じ引数で呼んだ時、返り値は等しい(=副作用はない)。</description>
			/// </item>
			/// <item>
			/// <description><paramref name="g"/>(<see cref="TOp.Identity"/>) = true</description>
			/// </item>
			/// <item>
			/// <description>0≤<paramref name="l"/>≤n</description>
			/// </item>
			/// </list>
			/// <para>計算量: O(log n)</para>
			/// </remarks>
			public int MaxRight(int l, Predicate<TValue> g)
			{
				Debug.Assert((uint)l <= Length);
				Debug.Assert(g(op.Identity));
				if (l == Length) return Length;
				l += size;
				for (int i = log; i >= 1; i--) Push(l >> i);
				TValue sm = op.Identity;
				do
				{
					while (l % 2 == 0) l >>= 1;
					if (!g(op.Operate(sm, d[l])))
					{
						while (l < size)
						{
							Push(l);
							l = (2 * l);
							if (g(op.Operate(sm, d[l])))
							{
								sm = op.Operate(sm, d[l]);
								l++;
							}
						}
						return l - size;
					}
					sm = op.Operate(sm, d[l]);
					l++;
				} while ((l & -l) != l);

				return Length;
			}

			/// <summary>
			/// 以下の条件を両方満たす r を(いずれか一つ)返します。
			/// <list type="bullet">
			/// <item>
			/// <description>l = <paramref name="r"/> もしくは <paramref name="g"/>(op(a[l], a[l + 1], ..., a[<paramref name="r"/> - 1])) = true</description>
			/// </item>
			/// <item>
			/// <description>l = 0 もしくは <paramref name="g"/>(op(a[l - 1], a[l], ..., a[<paramref name="r"/> - 1])) = false</description>
			/// </item>
			/// </list>
			/// <para><paramref name="g"/> が単調だとすれば、<paramref name="g"/>(op(a[l], a[l + 1], ..., a[<paramref name="r"/> - 1])) = true となる最小の l、と解釈することが可能です。</para>
			/// </summary>
			/// <remarks>
			/// 制約
			/// <list type="bullet">
			/// <item>
			/// <description><paramref name="g"/> を同じ引数で呼んだ時、返り値は等しい(=副作用はない)。</description>
			/// </item>
			/// <item>
			/// <description><paramref name="g"/>(<see cref="TOp.Identity"/>) = true</description>
			/// </item>
			/// <item>
			/// <description>0≤<paramref name="r"/>≤n</description>
			/// </item>
			/// </list>
			/// <para>計算量: O(log n)</para>
			/// </remarks>
			public int MinLeft(int r, Predicate<TValue> g)
			{
				Debug.Assert((uint)r <= Length);
				Debug.Assert(g(op.Identity));
				if (r == 0) return 0;
				r += size;
				for (int i = log; i >= 1; i--) Push((r - 1) >> i);
				TValue sm = op.Identity;
				do
				{
					r--;
					while (r > 1 && (r % 2) != 0) r >>= 1;
					if (!g(op.Operate(d[r], sm)))
					{
						while (r < size)
						{
							Push(r);
							r = (2 * r + 1);
							if (g(op.Operate(d[r], sm)))
							{
								sm = op.Operate(d[r], sm);
								r--;
							}
						}
						return r + 1 - size;
					}
					sm = op.Operate(d[r], sm);
				} while ((r & -r) != r);
				return 0;
			}


			[DebuggerDisplay("Value = {" + nameof(value) + "}, Lazy = {" + nameof(lazy) + "}", Name = "{" + nameof(key) + ",nq}")]
			private struct DebugItem
			{
				public DebugItem(int l, int r, TValue value, F lazy)
				{
					if (r - l == 1)
						key = $"[{l}]";
					else
						key = $"[{l}-{r})";
					this.value = value;
					this.lazy = lazy;
				}
				[DebuggerBrowsable(DebuggerBrowsableState.Never)]
				private readonly string key;
				[DebuggerBrowsable(DebuggerBrowsableState.Never)]
				private readonly TValue value;
				[DebuggerBrowsable(DebuggerBrowsableState.Never)]
				private readonly F lazy;
			}
			private class DebugView
			{
				private readonly LazySegtree<TValue, F, TOp> segtree;
				public DebugView(LazySegtree<TValue, F, TOp> segtree)
				{
					this.segtree = segtree;
				}
				[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
				public DebugItem[] Items
				{
					get
					{
						var items = new List<DebugItem>(segtree.Length);
						for (int len = segtree.size; len > 0; len >>= 1)
						{
							int unit = segtree.size / len;
							for (int i = 0; i < len; i++)
							{
								int l = i * unit;
								int r = System.Math.Min(l + unit, segtree.Length);
								if (l < segtree.Length)
								{
									int dataIndex = i + len;
									if ((uint)dataIndex < segtree.lz.Length)
										items.Add(new DebugItem(l, r, segtree.d[dataIndex], segtree.lz[dataIndex]));
									else
										items.Add(new DebugItem(l, r, segtree.d[dataIndex], op.FIdentity));
								}
							}
						}
						return items.ToArray();
					}
				}
			}


			/// <summary>
			/// Debug ビルドにおいて、Monoid が正しいかチェックする。
			/// </summary>
			/// <param name="value"></param>
			[Conditional("DEBUG")]
			public static void AssertMonoid(TValue value)
			{
				//	Debug.Assert(op.Operate(value, op.Identity).Equals(value),
				//		$"{nameof(op.Operate)}({value}, {op.Identity}) != {value}");
				//	Debug.Assert(op.Operate(op.Identity, value).Equals(value),
				//		$"{nameof(op.Operate)}({op.Identity}, {value}) != {value}");
			}

			/// <summary>
			/// Debug ビルドにおいて、FIdentity が恒等写像かチェックする。
			/// </summary>
			/// <param name="value"></param>
			[Conditional("DEBUG")]
			public static void AssertFIdentity(TValue value)
			{
				Debug.Assert(op.Mapping(op.FIdentity, value).Equals(value),
					$"{nameof(op.Mapping)}({op.Identity}, {value}) != {value}");
			}

			/// <summary>
			/// Debug ビルドにおいて、F が分配法則を満たすかチェックする。
			/// </summary>
			/// <param name="value"></param>
			[Conditional("DEBUG")]
			public static void AssertF(F f, TValue v1, TValue v2)
			{
				Debug.Assert(op.Mapping(op.FIdentity, op.Operate(v1, v2)).Equals(op.Operate(op.Mapping(op.FIdentity, v1), op.Mapping(op.FIdentity, v2))),
					$"{nameof(op.Mapping)}({nameof(op.Operate)}({v1}, {v2})) != {nameof(op.Operate)}({nameof(op.Mapping)}({op.Identity}, {v1}), {nameof(op.Mapping)}({op.Identity}, {v2}))");
			}
		}

		public static partial class InternalMath
		{
			/// <summary>
			/// <paramref name="n"/> ≤ 2**x を満たす最小のx
			/// </summary>
			/// <remarks>
			/// <para>制約: 0≤<paramref name="n"/></para>
			/// </remarks>
			public static int CeilPow2(int n)
			{
				var un = (uint)n;
				if (un <= 1) return 0;
				return BitOperations.Log2(un - 1) + 1;
			}
		}
	}

	static class Extention
	{
		public static string Join<T>(this IEnumerable<T> x, string separator = "") => string.Join(separator, x);

		public static int UpperBound<T>(this IList<T> list, T value) => list.BinarySearch(value, true, 0, list.Count, Comparer<T>.Default);
		public static int LowerBound<T>(this IList<T> list, T value) => list.BinarySearch(value, false, 0, list.Count, Comparer<T>.Default);
		public static int BinarySearch<T>(this IList<T> list, T value, bool isUpperBound, int index, int length, Comparer<T> comparer)
		{
			var ng = index - 1;
			var ok = index + length;
			while (ok - ng > 1)
			{
				var mid = ng + (ok - ng) / 2;
				var res = comparer.Compare(list[mid], value);
				if (res < 0 || (isUpperBound && res == 0)) ng = mid;
				else ok = mid;
			}
			return ok;
		}
	}

	class Scanner
	{
		string[] s;
		int i;

		char[] separator = new char[] { ' ' };

		public Scanner()
		{
			s = new string[0];
			i = 0;
		}

		public string Read() => ReadString();

		public string ReadString()
		{
			if (i < s.Length) return s[i++];
			string st = Console.ReadLine();
			while (st == "") st = Console.ReadLine();
			s = st.Split(separator, StringSplitOptions.RemoveEmptyEntries);
			if (s.Length == 0) return ReadString();
			i = 0;
			return s[i++];
		}

		public string[] ReadStringArray(int N)
		{
			string[] Array = new string[N];
			for (int i = 0; i < N; i++)
			{
				Array[i] = ReadString();
			}
			return Array;
		}

		public int ReadInt()
		{
			return int.Parse(ReadString());
		}

		public int[] ReadIntArray(int N, int add = 0)
		{
			int[] Array = new int[N];
			for (int i = 0; i < N; i++)
			{
				Array[i] = ReadInt() + add;
			}
			return Array;
		}

		public long ReadLong()
		{
			return long.Parse(ReadString());
		}

		public long[] ReadLongArray(int N, long add = 0)
		{
			long[] Array = new long[N];
			for (int i = 0; i < N; i++)
			{
				Array[i] = ReadLong() + add;
			}
			return Array;
		}

		public double ReadDouble()
		{
			return double.Parse(ReadString());
		}

		public double[] ReadDoubleArray(int N, double add = 0)
		{
			double[] Array = new double[N];
			for (int i = 0; i < N; i++)
			{
				Array[i] = ReadDouble() + add;
			}
			return Array;
		}

		public T1 ReadValue<T1>() => (T1)Convert.ChangeType(ReadString(), typeof(T1));

		public (T1, T2) ReadValue<T1, T2>()
		{
			var inputs = ReadStringArray(2);
			var v1 = (T1)Convert.ChangeType(inputs[0], typeof(T1));
			var v2 = (T2)Convert.ChangeType(inputs[1], typeof(T2));
			return (v1, v2);
		}

		public (T1, T2, T3) ReadValue<T1, T2, T3>()
		{
			var inputs = ReadStringArray(3);
			var v1 = (T1)Convert.ChangeType(inputs[0], typeof(T1));
			var v2 = (T2)Convert.ChangeType(inputs[1], typeof(T2));
			var v3 = (T3)Convert.ChangeType(inputs[2], typeof(T3));
			return (v1, v2, v3);
		}

		public (T1, T2, T3, T4) ReadValue<T1, T2, T3, T4>()
		{
			var inputs = ReadStringArray(4);
			var v1 = (T1)Convert.ChangeType(inputs[0], typeof(T1));
			var v2 = (T2)Convert.ChangeType(inputs[1], typeof(T2));
			var v3 = (T3)Convert.ChangeType(inputs[2], typeof(T3));
			var v4 = (T4)Convert.ChangeType(inputs[3], typeof(T4));
			return (v1, v2, v3, v4);
		}

		public (T1, T2, T3, T4, T5) ReadValue<T1, T2, T3, T4, T5>()
		{
			var inputs = ReadStringArray(5);
			var v1 = (T1)Convert.ChangeType(inputs[0], typeof(T1));
			var v2 = (T2)Convert.ChangeType(inputs[1], typeof(T2));
			var v3 = (T3)Convert.ChangeType(inputs[2], typeof(T3));
			var v4 = (T4)Convert.ChangeType(inputs[3], typeof(T4));
			var v5 = (T5)Convert.ChangeType(inputs[4], typeof(T5));
			return (v1, v2, v3, v4, v5);
		}

		public (T1, T2, T3, T4, T5, T6) ReadValue<T1, T2, T3, T4, T5, T6>()
		{
			var inputs = ReadStringArray(6);
			var v1 = (T1)Convert.ChangeType(inputs[0], typeof(T1));
			var v2 = (T2)Convert.ChangeType(inputs[1], typeof(T2));
			var v3 = (T3)Convert.ChangeType(inputs[2], typeof(T3));
			var v4 = (T4)Convert.ChangeType(inputs[3], typeof(T4));
			var v5 = (T5)Convert.ChangeType(inputs[4], typeof(T5));
			var v6 = (T6)Convert.ChangeType(inputs[5], typeof(T6));
			return (v1, v2, v3, v4, v5, v6);
		}

		public (T1, T2, T3, T4, T5, T6, T7) ReadValue<T1, T2, T3, T4, T5, T6, T7>()
		{
			var inputs = ReadStringArray(7);
			var v1 = (T1)Convert.ChangeType(inputs[0], typeof(T1));
			var v2 = (T2)Convert.ChangeType(inputs[1], typeof(T2));
			var v3 = (T3)Convert.ChangeType(inputs[2], typeof(T3));
			var v4 = (T4)Convert.ChangeType(inputs[3], typeof(T4));
			var v5 = (T5)Convert.ChangeType(inputs[4], typeof(T5));
			var v6 = (T6)Convert.ChangeType(inputs[5], typeof(T6));
			var v7 = (T7)Convert.ChangeType(inputs[6], typeof(T7));
			return (v1, v2, v3, v4, v5, v6, v7);
		}

		public T1[] ReadValueArray<T1>(int N)
		{
			var v1 = new T1[N];
			for (int i = 0; i < N; i++)
			{
				v1[i] = ReadValue<T1>();
			}
			return v1;
		}

		public (T1[], T2[]) ReadValueArray<T1, T2>(int N)
		{
			var (v1, v2) = (new T1[N], new T2[N]);
			for (int i = 0; i < N; i++)
			{
				var (t1, t2) = ReadValue<T1, T2>();
				v1[i] = t1;
				v2[i] = t2;
			}
			return (v1, v2);
		}

		public (T1[], T2[], T3[]) ReadValueArray<T1, T2, T3>(int N)
		{
			var (v1, v2, v3) = (new T1[N], new T2[N], new T3[N]);
			for (int i = 0; i < N; i++)
			{
				var (t1, t2, t3) = ReadValue<T1, T2, T3>();
				v1[i] = t1;
				v2[i] = t2;
				v3[i] = t3;
			}
			return (v1, v2, v3);
		}

		public (T1[], T2[], T3[], T4[]) ReadValueArray<T1, T2, T3, T4>(int N)
		{
			var (v1, v2, v3, v4) = (new T1[N], new T2[N], new T3[N], new T4[N]);
			for (int i = 0; i < N; i++)
			{
				var (t1, t2, t3, t4) = ReadValue<T1, T2, T3, T4>();
				v1[i] = t1;
				v2[i] = t2;
				v3[i] = t3;
				v4[i] = t4;
			}
			return (v1, v2, v3, v4);
		}

		public (T1[], T2[], T3[], T4[], T5[]) ReadValueArray<T1, T2, T3, T4, T5>(int N)
		{
			var (v1, v2, v3, v4, v5) = (new T1[N], new T2[N], new T3[N], new T4[N], new T5[N]);
			for (int i = 0; i < N; i++)
			{
				var (t1, t2, t3, t4, t5) = ReadValue<T1, T2, T3, T4, T5>();
				v1[i] = t1;
				v2[i] = t2;
				v3[i] = t3;
				v4[i] = t4;
				v5[i] = t5;
			}
			return (v1, v2, v3, v4, v5);
		}

		public (T1[], T2[], T3[], T4[], T5[], T6[]) ReadValueArray<T1, T2, T3, T4, T5, T6>(int N)
		{
			var (v1, v2, v3, v4, v5, v6) = (new T1[N], new T2[N], new T3[N], new T4[N], new T5[N], new T6[N]);
			for (int i = 0; i < N; i++)
			{
				var (t1, t2, t3, t4, t5, t6) = ReadValue<T1, T2, T3, T4, T5, T6>();
				v1[i] = t1;
				v2[i] = t2;
				v3[i] = t3;
				v4[i] = t4;
				v5[i] = t5;
				v6[i] = t6;
			}
			return (v1, v2, v3, v4, v5, v6);
		}

		public (T1[], T2[], T3[], T4[], T5[], T6[], T7[]) ReadValueArray<T1, T2, T3, T4, T5, T6, T7>(int N)
		{
			var (v1, v2, v3, v4, v5, v6, v7) = (new T1[N], new T2[N], new T3[N], new T4[N], new T5[N], new T6[N], new T7[N]);
			for (int i = 0; i < N; i++)
			{
				var (t1, t2, t3, t4, t5, t6, t7) = ReadValue<T1, T2, T3, T4, T5, T6, T7>();
				v1[i] = t1;
				v2[i] = t2;
				v3[i] = t3;
				v4[i] = t4;
				v5[i] = t5;
				v6[i] = t6;
				v7[i] = t7;
			}
			return (v1, v2, v3, v4, v5, v6, v7);
		}
	}
}