Problem:
Score : 100 points

Problem Statement

You are given an array a_0, a_1, ..., a_{N-1} of length N. Process Q queries of the following types.

The type of i-th query is represented by T_i.

T_i=1: You are given two integers X_i,V_i. Replace the value of A_{X_i} with V_i.

T_i=2: You are given two integers L_i,R_i. Calculate the maximum value among A_{L_i},A_{L_i+1},\cdots,A_{R_i}.

T_i=3: You are given two integers X_i,V_i. Calculate the minimum j such that X_i \leq j \leq N, V_i \leq A_j. If there is no such j, answer j=N+1 instead.

Constraints

1 \leq N \leq 2 \times 10^5

0 \leq A_i \leq 10^9

1 \leq Q \leq 2 \times 10^5

1 \leq T_i \leq 3

1 \leq X_i \leq N, 0 \leq V_i \leq 10^9 (T_i=1,3)

1 \leq L_i \leq R_i \leq N (T_i=2)

All values in Input are integer.

Input

Input is given from Standard Input in the following format:

N Q
A_1 A_2 \cdots A_N
First query
Second query
\vdots
Q-th query

Each query is given in the following format:

If T_i=1,3,

T_i X_i V_i

If T_i=2,

T_i L_i R_i

Output

For each query with T_i=2, 3, print the answer.

Sample Input 1

5 5
1 2 3 2 1
2 1 5
3 2 3
1 3 1
2 2 4
3 1 3

Sample Output 1

3
3
2
6

First query: Print 3, which is the maximum of (A_1,A_2,A_3,A_4,A_5)=(1,2,3,2,1).

Second query: Since 3>A_2, j=2 does not satisfy the condition．Since 3 \leq A_3, print j=3.

Third query: Replace the value of A_3 with 1. It becomes A=(1,2,1,2,1).

Fourth query: Print 2, which is the maximum of (A_2,A_3,A_4)=(2,1,2).

Fifth query: Since there is no j that satisfies the condition, print j=N+1=6.

Solution in Java (OpenJDK 11.0.6) (judge verdict: Accepted)

import java.util.Arrays;
import java.util.Scanner;
import java.util.function.BinaryOperator;
import java.util.function.Predicate;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int Q = sc.nextInt();
        Integer[] A = new Integer[n];
        for (int i = 0; i < n; i++) {
            A[i] = sc.nextInt();
        }
        SegTree<Integer> seg = new SegTree<Integer>(A, -1, Integer::max);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < Q; i++) {
            int q = sc.nextInt();
            if(1 == q){
                int k = sc.nextInt()-1, a = sc.nextInt() ;
                seg.set(k, a);
            }else if(2 == q){
                int l = sc.nextInt()-1, r = sc.nextInt();
                sb.append(seg.query(l, r) + "\n");
            }else{
                int l = sc.nextInt()-1, v = sc.nextInt();
                int j = seg.maxRight(l, x -> x < v) + 1;
                sb.append(j + "\n");
            }
        }
        System.out.println(sb.toString());
        sc.close();

    }

}

class SegTree<T> {

    /** SegTreeで扱う要素数 */
    private final int N;
    /** nより大きい最小の2の累乗数 */
    private final int SIZE;
    /** 単位元 */
    private final T E;
    /** 演算 */
    private final BinaryOperator<T> OP;
    /**
     * 要素全体を含むノードのindex：0.
     * k番目(0-indexed)の要素のindex：k + size - 1
     * ノードkの親要素のindex：(k - 1) / 2.
     * ノードkの子要素のうち左側のindex：k*2+1.
     * ノードkの子要素のうち右側のindex：k*2+2.
     */
    private final T[] DATA;

    /**
     * 全てのノードを単位元で初期化するコンストラクタ.<br>
     * 計算量：O(N)
     * @param n 要素数
     * @param e 単位元
     * @param op 演算
     */
    @SuppressWarnings("unchecked")
    public SegTree(int n, T e, BinaryOperator<T> op){
        int tmpSize = 1;
        while(tmpSize < n) tmpSize *= 2;
        this.N = n;
        this.E = e;
        this.OP = op;
        this.SIZE = tmpSize;
        this.DATA = (T[]) new Object[SIZE*2];
        Arrays.fill(DATA, e);
    }

    /**
     * 引数で指定された配列で初期化するコンストラクタ.<br>
     * 計算量：O(N)
     * @param a 初期配列
     * @param e 単位元
     * @param op 演算
     */
    @SuppressWarnings("unchecked")
    public SegTree(T[] a, T e, BinaryOperator<T> op){
        int tmpSize = 1;
        while(tmpSize < a.length) tmpSize *= 2;
        this.N = a.length;
        this.E = e;
        this.OP = op;
        this.SIZE = tmpSize;
        this.DATA = (T[]) new Object[SIZE*2];
        Arrays.fill(DATA, e);
        for (int i = 0; i < N; i++) DATA[i + SIZE - 1] = a[i];
        for (int i = SIZE-2; i >= 0; i--) update(i);
    }

    /**
     * k番目の要素を取得する.
     * 計算量：O(1)
     * @param k
     * @return k番目の要素の値
     */
    public T get(int k) {
        return DATA[k + SIZE - 1];
    }

    /**
     * k番目（0-indexed）の要素をaに更新する.<br>
     * 計算量：O(logN)
     * @param k 変更対象要素のindex（0-indexed）
     * @param v 変更後の値
     */
    public void set(int k, T v){
        k += SIZE - 1;
        DATA[k] = v;
        while(0 < k){
            k = (k - 1) / 2;
            update(k);
        }
    }

    /**
     * ノードkに演算を適用する.<br>
     * 計算量：O(1)
     * @param k
     */
    private void update(int k) {
        DATA[k] = OP.apply(DATA[k * 2 + 1], DATA[k * 2 + 2]);
    }

    /**
     * 区間[a, b)の結果を取得する.<br>
     * 計算量：O(logN)
     * @param l 求める区間の下限（含む）
     * @param r 求める区間の上限（含まない）
     * @return 区間[a, b)の結果
     */
    public T query(int l, int r){
        return query(l, r, 0, 0, SIZE);
    }

    /**
     * 区間[a, b)の結果を取得する.<br>
     * @param l 求める区間の下限（含む）
     * @param r 求める区間の上限（含まない）
     * @param k 確認するノードのindex
     * @param a 確認するノードの下限（含む）
     * @param b 確認するノードの上限（含まない）
     * @return 区間[a, b)の結果
     */
    private T query(int l, int r, int k, int a, int b){
        if(b <= l || r <= a) return E;

        if(l <= a && b <= r) return DATA[k];

        T vl = query(l, r, k*2+1, a, (a+b)/2);
        T vr = query(l, r, k*2+2, (a+b)/2, b);
        return OP.apply(vl, vr);
    }

    /**
     * pr(op(l, l+1, ... , x-1)) = true かつ
     * pr(op(l, l+1, ... , x)) = false
     * を満たす最大のxを求める.<br>
     * @param l 対象範囲下限
     * @param pr 判定条件
     * @return 条件を満たす最大のIndex
     */
    public int maxRight(int l, Predicate<T> pr) {
        return maxRight(l, N, pr);
    }

    /**
     * [l, r)の範囲において
     * pr(op(l, l+1, ... , x-1)) = true かつ
     * pr(op(l, l+1, ... , x)) = false
     * を満たす最大のxを求める.<br>
     * @param l 対象範囲下限（含む）
     * @param r 対象範囲上限（含まない）
     * @param pr 判定条件
     * @return 条件を満たす最大のIndex
     */
    public int maxRight(int l, int r, Predicate<T> pr) {
        int idx = l + SIZE - 1;
        int li = l, ri = l, width = 1;
        T applied = E;
        while(ri < SIZE) {
            T tmp = OP.apply(applied, DATA[idx]);
            if(ri < r && pr.test(tmp)) {
                applied = tmp;
                if((idx&1) == 1) {
                    idx++;
                }else {
                    idx = (idx-1)/2 +1;
                    width *= 2;
                }
                li = ri + 1;
                ri = li + width -1;
            }else {
                if(SIZE - 1 <= idx) {
                    return li;
                }
                idx = idx*2+1;
                width /= 2;
                ri -= width;
            }
        }
        return r;
    }

    // TODO minLeft()
}