Problem:
Range Minimum Query (RMQ)

Write a program which manipulates a sequence A = {a0, a1, . . . , an-1} with the following operations:

find(s, t): report the minimum element in as, as+1, . . . ,at.

update(i, x): change ai to x.

Note that the initial values of ai (i = 0, 1, . . . , n−1) are 231-1.

Input

n q
com0 x0 y0
com1 x1 y1
...
comq−1 xq−1 yq−1

In the first line, n (the number of elements in A) and q (the number of queries) are given. Then, q queries are given where com represents the type of queries. '0' denotes update(xi, yi) and '1' denotes find(xi, yi).

Output

For each find operation, print the minimum element.

Constraints

1 ≤ n ≤ 100000

1 ≤ q ≤ 100000

If comi is 0, then 0 ≤ xi < n, 0 ≤ yi < 231-1.

If comi is 1, then 0 ≤ xi < n, 0 ≤ yi < n.

Sample Input 1

3 5
0 0 1
0 1 2
0 2 3
1 0 2
1 1 2

Sample Output 1

1
2

Sample Input 2

1 3
1 0 0
0 0 5
1 0 0

Sample Output 2

2147483647
5

Solution in C# (judge verdict: Accepted)

// optional
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
class Main {
    class SegTree {
        private const long MAX = int.MaxValue;
        long[] nodes;
        int l = 1;
        public SegTree(int n) {
            while (l < n) {
                l *= 2;
            }

            this.nodes = new long[2 * l - 1];
            for (int i = 0; i < nodes.Length; i++) {
                nodes[i] = MAX;
            }
        }

        public void Update(int i, long x) {
            int cur = l - 1 + i;
            nodes[cur] = x;
            
            // 親に向かって更新
            while (cur > 0) {
                var p = (cur - 1) / 2;
                nodes[p] = Math.Min(nodes[2 * p + 1], nodes[2 * p + 2]);
                cur = p;
            }
        }

        public long Search(int searchL, int searchR) {
            return Search(searchL, searchR, 0, 0, l);
        }

        private long Search(int searchL, int searchR, int nowP, int nowL, int nowR) {
            if (searchL >= nowR || searchR <= nowL) {
                return MAX;
            }

            if (searchL <= nowL && nowR <= searchR) {
                return nodes[nowP];
            }

            return Math.Min(
                Search(searchL, searchR, 2 * nowP + 1, nowL, (nowL + nowR) / 2),
                Search(searchL, searchR, 2 * nowP + 2, (nowL + nowR) / 2, nowR)
                );
        }
    }

    public void Run() {
        var nq = ReadLib.ReadAsIntArray();
        var tree = new SegTree(nq[0]);
        for (int i = 0; i < nq[1]; i++) {
            var cxy = ReadLib.ReadAsIntArray();
            if (cxy[0] == 0) {
                tree.Update(cxy[1], cxy[2]);
            }

            if (cxy[0] == 1) {
                Console.WriteLine(tree.Search(cxy[1], cxy[2] + 1));
            }
        }
    }
}



static class ReadLib {
    public static long ReadAsLong() {
        return long.Parse(Console.ReadLine());
    }

    public static int ReadAsInt() {
        return int.Parse(Console.ReadLine());
    }

    public static int[] ReadAsIntArray() {
        return Array.ConvertAll(Console.ReadLine().Split(' '), int.Parse);
    }

    public static long[] ReadAsLongArray() {
        return Array.ConvertAll(Console.ReadLine().Split(' '), long.Parse);
    }

    public static ulong[] ReadAsULongArray() {
        return Console.ReadLine().Split(' ').Select(s => ulong.Parse(s)).ToArray();
    }

    public static string[] ReadAsStringArray() {
        return Console.ReadLine().Split(' ');
    }

    public static int[] ReadNTimesAsInt(int n) {
        return Enumerable.Range(1, n).Select(s => int.Parse(Console.ReadLine())).ToArray();
    }

    public static char[][] ReadNTimesAsCharArray(int n) {
        return Enumerable.Range(1, n).Select(i => Console.ReadLine().ToArray()).ToArray();
    }

    public static long[] ReadNTimesAsLong(int n) {
        return Enumerable.Range(1, n).Select(s => long.Parse(Console.ReadLine())).ToArray();
    }

    public static int[][] ReadNTimesAsIntArray(int n) {
        return Enumerable.Range(1, n).Select(s => ReadAsIntArray()).ToArray();
    }

    public static string[] ReadNTimesAsString(int n) {
        return Enumerable.Range(1, n).Select(s => Console.ReadLine()).ToArray();
    }

    public static char[,] ReadMapAsChar(int hSize, int wSize) {
        var map = new char[hSize, wSize];
        for (int h = 0; h < hSize; h++) {
            var a = Console.ReadLine();
            for (int w = 0; w < wSize; w++) {
                map[h, w] = a[w];
            }
        }
        return map;
    }
}
/// <summary>優先度付きキュー</summary>
class PriorityQueue<T> : IEnumerable<T>, IReadOnlyCollection<T> where T : IComparable<T> {
    public List<T> HeapArray { get; }
    public T NIL { get; }
    public bool IsMax { get; }

    public int Count => HeapArray.Count;

    /// <param name="heapArray">ヒープ</param>
    /// <param name="NIL">NILの値</param>
    /// <param name="IsMax">True時：大きい方から取り出す、False時：小さい方から取り出す</param>
    public PriorityQueue(T NIL, IEnumerable<T> heapArray = null, bool IsMax = true) {
        this.HeapArray = heapArray == null ? new List<T>() : heapArray.ToList();
        this.NIL = NIL;
        this.IsMax = IsMax;
        Update();
    }

    /// <summary>KeyのValueを取得</summary>
    public T this[int id] {
        get {
            if (id <= 0 || id > HeapArray.Count) {
                return NIL;
            }

            return HeapArray[id - 1];
        }
    }

    class Node {
        public int Key { get; }
        public T Value => outer[Key];
        readonly PriorityQueue<T> outer;

        public Node(int key, PriorityQueue<T> outer) {
            Key = key;
            this.outer = outer;
        }

        public Node Left {
            get {
                var leftKey = 2 * Key;
                return new Node(leftKey, outer);
            }
        }

        public Node Right {
            get {
                var rightkey = 2 * Key + 1;
                return new Node(rightkey, outer);
            }
        }

        public Node Parent {
            get {
                var parentKey = Key / 2;
                return new Node(parentKey, outer);
            }
        }
    }

    private Node GetNode(int key) {
        return new Node(key, this);
    }

    /// <summary>max Heap木とする O(N)</summary>
    public void Update() {
        var n = HeapArray.Count() / 2;
        /// <summary>葉から順に処理を行う</summary>
        for (int i = n; i >= 1; i--) {
            Heapify(i);
        }
    }

    /// <summary>idを根とする部分木について、Heap木を維持したまま、idを下降させる</summary>
    private void Heapify(int key) {
        var current = GetNode(key);

        var left = current.Left;
        var right = current.Right;

        var largest = current;
        var sign = IsMax ? 1 : -1;
        if (left.Value.CompareTo(NIL) != 0 && left.Value.CompareTo(current.Value) * sign > 0) {
            largest = left;
        }

        if (right.Value.CompareTo(NIL) != 0 && right.Value.CompareTo(largest.Value) * sign > 0) {
            largest = right;
        }

        if (largest.Key != key) {
            HeapArray.Swap(key - 1, largest.Key - 1);   // 元と異なるときはSwapする
            Heapify(largest.Key); // largest.Key は元のkeyの場所。葉で更にMaxHeaptyを行う。
        }
    }

    /// <summary>keyの値を挿入する</summary>
    public void Enqueue(T value) {
        HeapArray.Add(value);

        var inserted = GetNode(HeapArray.Count);    // 末尾に挿入
        var parent = inserted.Parent;
        var sign = IsMax ? 1 : -1;

        // 挿入Nodeが根になる or 親 > 子になるまで親とNodeを交換
        while (inserted.Key > 1 && value.CompareTo(parent.Value) * sign > 0) {
            HeapArray.Swap(inserted.Key - 1, parent.Key - 1);
            inserted = parent;
            parent = inserted.Parent;
        }
    }

    public T Dequeue() {
        if (HeapArray.Count == 0) {
            return NIL;
        }

        var popVal = HeapArray.First();
        var newRootVal = HeapArray.Last();  // 新しい根となる。

        HeapArray.RemoveAt(HeapArray.Count - 1);    // del last

        if (HeapArray.Count > 0) {
            HeapArray[0] = newRootVal;
            Heapify(1);
        }

        return popVal;
    }

    public T Peek() {
        return HeapArray.First();
    }

    public IEnumerator<T> GetEnumerator() {
        while (HeapArray.Count > 0) {
            yield return Dequeue();
        }
    }

    IEnumerator IEnumerable.GetEnumerator() {
        return GetEnumerator();
    }

    public void Clear() {
        HeapArray.Clear();
    }

    public bool Contains(T val) {
        return HeapArray.Contains(val);
    }

    public PriorityQueue<T> Clone() {
        return new PriorityQueue<T>(NIL, HeapArray.DeepCopy());
    }
}
/// <summary>1次元NDTree</summary>
class OneDTree<T> where T : IComparable<T> {
    private T[] p;

    public OneDTree(T[] p) {
        this.p = p;
        this.nodes = new Node[p.Length];
        for (int i = 0; i < p.Length; i++) {
            nodes[i] = new Node();
        }

        MakeTree(0, p.Length);
    }

    static readonly int NIL = -1;

    // l以上、r未満の範囲で二分探索木を構成する
    class Node {
        public int Location { get; set; }
        public int Left { get; set; }
        public int Right { get; set; }

        public Node() {
            Location = NIL;
            Left = NIL;
            Right = NIL;
        }
    }

    Node[] nodes;
    int nodesP = 0; // TODO : スコープが広いので、C#のver upしたら関数内関数にする。
    private int MakeTree(int l, int r) {
        if (l >= r) {
            return NIL;
        }

        Array.Sort(p, l, r - l);
        var mid = (l + r) / 2;
        var cP = nodesP++;

        // mid以外の探索木を構成
        nodes[cP].Location = mid;
        nodes[cP].Left = MakeTree(l, mid);
        nodes[cP].Right = MakeTree(mid + 1, r);

        return cP;
    }
    /// <summary>
    /// 二分探索木からlower以上Higher以下の値を返す
    /// </summary>
    public IEnumerable<T> Find(T lower, T higher) {
        if (higher.CompareTo(lower) < 0) {
            throw new ArgumentException("higher < lower");
        }

        return Find(0, lower, higher);
    }

    /// <summary>
    /// 二分探索木からlower以上Higher以下の値を返す
    /// </summary>
    /// <param name="v">訪問中のnode番号</param>
    private IEnumerable<T> Find(int v, T lower, T higher) {
        var current = p[nodes[v].Location];

        if (current.CompareTo(lower) >= 0 && current.CompareTo(higher) <= 0) {
            yield return p[nodes[v].Location];
        }

        if (nodes[v].Left != NIL && current.CompareTo(lower) >= 0) {
            foreach (var item in Find(nodes[v].Left, lower, higher)) {
                yield return item;
            }
        }

        if (nodes[v].Right != NIL && current.CompareTo(higher) <= 0) {
            foreach (var item in Find(nodes[v].Right, lower, higher)) {
                yield return item;
            }
        }

    }
}
/// <summary>2DimentionTree</summary>
class TwoDTree {
    const int NIL = -1;
    struct Node {
        public int Left { get; set; }
        public int Right { get; set; }
        public int Location { get; set; }
        public Node(int location, int left, int right) {
            Location = location;
            Left = left;
            Right = right;
        }
    }

    [Serializable]
    public struct Point {
        public int Id { get; set; }
        public int X { get; set; }
        public int Y { get; set; }

        public Point(int id, int x, int y) {
            Id = id;
            X = x;
            Y = y;
        }
    }

    public static Point CreatePoint(int id, int x, int y) {
        return new Point(id, x, y);
    }

    readonly Node[] nodes;
    readonly Point[] points;
    public TwoDTree(Point[] points) {
        this.points = points;
        this.nodes = new Node[points.Length];
        for (int i = 0; i < points.Length; i++) {
            nodes[i] = new Node(NIL, NIL, NIL);
        }

        MakeTree(0, points.Length, 0);
    }

    class CompX : IComparer<Point> {
        public int Compare(Point p1, Point p2) {
            return p1.X.CompareTo(p2.X);
        }
    }

    class CompY : IComparer<Point> {
        public int Compare(Point p1, Point p2) {
            return p1.Y.CompareTo(p2.Y);
        }
    }

    readonly CompX compX = new CompX();
    readonly CompY compY = new CompY();
    int cp = 0;

    private int MakeTree(int left, int right, int depth) {
        if (left >= right) {
            return NIL;
        }

        if (depth % 2 == 0) {
            Array.Sort(points, left, right - left, compX);
        } else {
            Array.Sort(points, left, right - left, compY);
        }

        var mid = (left + right) / 2;
        var cpp = cp++;
        nodes[cpp].Location = mid;
        nodes[cpp].Left = MakeTree(left, mid, depth + 1);
        nodes[cpp].Right = MakeTree(mid + 1, right, depth + 1);

        return cpp;
    }

    /// <summary>minXとmaxX、minYとmaxYの間の点をResultに格納する。</summary>
    public void Find(int minX, int maxX, int minY, int maxY) {
        Find(0, minX, maxX, minY, maxY, 0);
    }

    public void ClearResult() {
        result.Clear();
    }

    /// <summary>Findの結果を得る</summary>
    public List<Point> GetFindResult() {
        return result;
    }

    List<Point> result = new List<Point>();
    private void Find(int nodeP, int minX, int maxX, int minY, int maxY, int depth) {
        var node = nodes[nodeP];
        var p = points[node.Location];

        if (p.X >= minX && p.Y >= minY && p.X <= maxX && p.Y <= maxY) {
            result.Add(p);
        }

        if (depth % 2 == 0) {
            if (node.Left != NIL && p.X >= minX) {
                Find(node.Left, minX, maxX, minY, maxY, depth + 1);
            }

            if (node.Right != NIL && p.X <= maxX) {
                Find(node.Right, minX, maxX, minY, maxY, depth + 1);
            }
        } else {
            if (node.Left != NIL && p.Y >= minY) {
                Find(node.Left, minX, maxX, minY, maxY, depth + 1);
            }

            if (node.Right != NIL && p.Y <= maxY) {
                Find(node.Right, minX, maxX, minY, maxY, depth + 1);
            }
        }
    }
}
/// <summary>拡張メソッドs</summary>
static class MyExtesntion {
    /// <summary>iとjを入れ替える</summary>
    public static void Swap<T>(this T[] arr, int i, int j) {
        var buf = arr[i];
        arr[i] = arr[j];
        arr[j] = buf;
    }

    public static void Swap<T>(this List<T> list, int i, int j) {
        var buf = list[i];
        list[i] = list[j];
        list[j] = buf;
    }

    /// <summary>
    /// partition。末尾sArr[r]を基準として、左に基準より小さい数、右に基準より大きい数が存在するように配列を並び替える。
    /// </summary>
    /// <param name="sArr"></param>
    /// <param name="p">partitionの先頭の添字</param>
    /// <param name="r">partitionの末尾の添字</param>
    /// <returns>partitionの基準が存在する添字</returns>
    public static int Partition(this int[] sArr, int p, int r) {
        var x = sArr[r];
        var i = p - 1;
        for (int j = p; j < r; j++) {
            if (sArr[j] <= x) {
                i++;
                sArr.Swap(i, j);
            }
        }
        sArr.Swap(i + 1, r);
        return i + 1;
    }

    /// <summary>深いコピーをする</summary>
    public static T DeepCopy<T>(this T obj) {
        T result;
        var b = new BinaryFormatter();
        using (var mem = new MemoryStream()) {
            b.Serialize(mem, obj); //  binaryFormatterによりobjをmemにserial化して書き出す
            mem.Position = 0; // memのpotisionを先頭に戻す
            result = (T)b.Deserialize(mem); // memの内容を読み取る。
        }
        return result;
    }

    public static string Left(this string str, int length) {
        return str.Substring(0, length);
    }

    public static string Right(this string str, int length) {
        return str.Substring(str.Length - length, length);
    }

    public static int Find<T>(this T[] array, T obj) {
        return Array.IndexOf(array, obj);
    }

    public static int LastFind<T>(this T[] array, T obj) {
        return Array.LastIndexOf(array, obj);
    }

    /// <summary>同一の値で埋める</summary>
    public static void Initialize<T>(this T[,] array, T value) {
        for (int i = 0; i < array.GetLength(0); i++) {
            for (int j = 0; j < array.GetLength(1); j++) {
                array[i, j] = value;
            }
        }
    }

    /// <summary>数え上げて、key, 出現回数に変換</summary>
    public static Dictionary<T, int> CountUp<T>(this IEnumerable<T> str) {
        return
            str
            .GroupBy(c => c)
            .Select(g => new KeyValuePair<T, int>(g.Key, g.Count()))
            .ToDictionary(e => e.Key, e => e.Value);
    }

    /// <summary>ある要素を持つindexをすべて返す</summary>
    public static int[] InducesOfValue<T>(this T[] arr, T searchValue) {
        return arr
            .Select((v, i) => new { v, i })
            .Where(o => searchValue.Equals(o.v))
            .Select(o => o.i)
            .ToArray();
    }

    /// <summary>ある要素を持つindexをすべて返す</summary>
    public static int[] Induces<T>(this IEnumerable<T> arr, T searchValue) {
        return arr.ToArray().InducesOfValue(searchValue);
    }
}

static class MyMath {
    /// <summary>階乗</summary>
    public static long Factorial(int n) {
        if (n == 0) {
            return 1;
        }

        return Permutation(n, 0);
    }

    public static long Permutation(int n, int k) {
        return Enumerable.Range(0, n - k).Select(i => n - i).Aggregate(1, (b, i) => b * i);
    }

    #region FullPower
    /// <summary>総乗、π</summary>
    public static long FullPower(IEnumerable<long> vs) {
        return vs.Aggregate(1L, (a, b) => a * b);
    }

    public static long FullPower(IEnumerable<int> vs) {
        return vs.Aggregate(1L, (a, b) => a * b);
    }
    #endregion

    /// <summary>最大公約数</summary>
    public static long Gcd(long item1, long item2) {
        var a = Math.Max(item1, item2);
        var b = Math.Min(item1, item2);

        while (true) {
            long mod;
            Math.DivRem(a, b, out mod);
            if (mod == 0) {
                break;
            }

            a = b;
            b = mod;
        }
        return b;
    }

    /// <summary>約分。最大公約数で割ったものを返す。</summary>
    public static Tuple<long, long> DeviedByGcd(long item1, long item2) {
        var gcd = Gcd(item1, item2);
        return new Tuple<long, long>(item1 / gcd, item2 / gcd);
    }

    public static long Lcm(int item1, int item2) {
        return Math.BigMul(item1, item2) / Gcd(item1, item2);
    }

    public static ModCombination ModCombination(int maxN, int mod = 1000000007) {
        return new ModCombination(maxN, mod);
    }

    /// <summary>a^n mod</summary>
    public static int ModPow(long a, long n, int mod = 1000000007) {
        long res = 1;
        long pow = a;

        while (n > 0) {
            if ((n & 1) == 1) {
                res = (pow * res) % mod;    // resにaをかける
            }

            pow = (pow * pow) % mod;    // 次のaはa^2
            n >>= 1;
        }

        return (int)res;
    }

    /// <summary>約数を列挙</summary>
    public static long[] Divisors(long n) {
        var ret = new List<long>();
        var sqrt = Math.Ceiling(Math.Sqrt(n));
        for (long i = 1; i <= sqrt; i++) {
            if (i * i > n) {
                continue;
            }

            if (n % i != 0) {
                continue;
            }

            ret.Add(i);

            if (n / i == i) {
                continue;
            }

            ret.Add(n / i);
        }

        return ret.ToArray();
    }

    /// <summary>Combination。nCk。O(nk)</summary>
    /// <see cref="http://caprest.hatenablog.com/entry/2016/05/29/181102"/>
    public static long Combination(int n, int k) {
        if (n < k) {
            return 0;
        }

        var dp = new long[n + 1][];   // nCkのdp
        for (int i = 0; i <= n; i++) {
            dp[i] = new long[n + 1];
        }

        for (int i = 0; i <= n; i++) {
            dp[i][0] = 1;
            dp[i][i] = 1;
        }

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j < i; j++) {
                // nCk = n-1Ck-1 + n-1Ck
                dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
            }
        }

        return dp[n][k];
    }
}

/// <summary>組み合わせ</summary>
///  * Combination, nCk mod p を求める
/// https://ja.wikipedia.org/wiki/%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%A9%E9%80%86%E6%95%B0
/// http://drken1215.hatenablog.com/entry/2018/06/08/210000
class ModCombination {
    private int mod;
    private int[] fac;
    private int[] finv;
    /// <summary>
    /// 
    /// </summary>
    /// <param name="nMax"></param>
    /// <param name="mod"></param>
    public ModCombination(int nMax, int mod = 1000000007) {
        this.mod = mod;
        fac = new int[nMax]; // modが法のn!の列
        fac[0] = 1; fac[1] = 1;

        finv = new int[nMax]; // modを法とした、invの累積の累積
        finv[0] = 1; finv[1] = 1;

        var inv = new int[nMax]; // indexの逆元（モジュラ逆数）の列
        inv[1] = 1;

        for (int i = 2; i < nMax; i++) {
            fac[i] = (int)(Math.BigMul(fac[i - 1], i) % mod);  // factorを求める
            // mod % i < i
            // modの倍数を加えても逆元である
            var s = Math.BigMul(inv[mod % i], mod / i) % mod;
            inv[i] = (int)(mod - s);　 // iに対する逆元を求める
            finv[i] = (int)(Math.BigMul(finv[i - 1], inv[i]) % mod);
        }
    }

    /// <summary>nCk</summary>
    public int Calculation(int n, int k) {
        if (n < k) return 0;
        if (n < 0 || k < 0) return 0;

        return (int)(Math.BigMul(fac[n], (int)(Math.BigMul(finv[k], finv[n - k]) % mod)) % mod);
    }
}

/// <summary>あまりを計算するためのintのラッパー</summary>
struct ModInt {
    int Value { get; }
    int Mod { get; }

    public override string ToString() {
        return Value.ToString();
    }

    public static explicit operator ModInt(long value) {
        return new ModInt(value);
    }

    public ModInt(long value, int mod = 1000000007) {
        this.Value = (int)(value % mod);
        this.Mod = mod;
    }

    public static explicit operator int(ModInt modInt) {
        return modInt.Value;
    }

    private static void CheckNotEqualMod(ModInt item1, ModInt item2) {
        if (item1.Mod != item2.Mod) {
            throw new Exception("mod not equal");
        }
    }

    public static ModInt operator +(ModInt item1, ModInt item2) {
        CheckNotEqualMod(item1, item2);
        long value = item1.Value + item2.Value;
        return new ModInt(value, item1.Mod);
    }

    public static ModInt operator *(ModInt item1, ModInt item2) {
        CheckNotEqualMod(item1, item2);
        long value = Math.BigMul(item1.Value, item2.Value);
        return new ModInt(value, item1.Mod);
    }

    public static ModInt operator -(ModInt item1, ModInt item2) {
        CheckNotEqualMod(item1, item2);
        long value = item1.Value - item2.Value;
        if (value < 0) {
            value += item1.Mod;
        }
        return new ModInt(value, item1.Mod);
    }

    public static ModInt operator +(ModInt item1, int item2) {
        return item1 + new ModInt(item2, item1.Mod);
    }

    public static ModInt operator *(ModInt item1, int item2) {
        return item1 * new ModInt(item2, item1.Mod);
    }

    public static ModInt operator -(ModInt item1, int item2) {
        return item1 - new ModInt(item2, item1.Mod);
    }


    public override int GetHashCode() {
        return this.Value;
    }

    public override bool Equals(object obj) {
        if (obj == null || this.GetType() != obj.GetType()) {
            return false;
        }

        var modIntObj = (ModInt)obj;
        return modIntObj.Value == this.Value;
    }

    public static bool operator ==(ModInt item1, ModInt item2) {
        return item1.Value == item2.Value;
    }

    public static bool operator !=(ModInt item1, ModInt item2) {
        return !(item1 == item2);
    }

    public static bool operator ==(ModInt item1, int item2) {
        return item1.Value == item2;
    }

    public static bool operator !=(ModInt item1, int item2) {
        return !(item1.Value == item2);
    }

    public static bool operator >=(ModInt item1, ModInt item2) {
        return item1.Value >= item2.Value;
    }

    public static bool operator <=(ModInt item1, ModInt item2) {
        return item1.Value <= item2.Value;
    }

    public static bool operator >=(ModInt item1, int item2) {
        return item1.Value >= item2;
    }

    public static bool operator <=(ModInt item1, int item2) {
        return item1.Value <= item2;
    }

    public static bool operator >(ModInt item1, ModInt item2) {
        return item1.Value > item2.Value;
    }

    public static bool operator <(ModInt item1, ModInt item2) {
        return item1.Value < item2.Value;
    }

    public static bool operator >(ModInt item1, int item2) {
        return item1.Value >= item2;
    }

    public static bool operator <(ModInt item1, int item2) {
        return item1.Value <= item2;
    }
}

public static class Program {
    public static void Main() {
        #region SetAutoFlushIsFalse
#if !DEBUG
        var sw = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false };
        Console.SetOut(sw);
#endif
        #endregion
        try {
            var main = new Main();
            main.Run();
        } finally {
            #region Flush
#if !DEBUG
        Console.Out.Flush();
        sw.Close();
#endif
            #endregion
        }
    }
}