package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

var sc, wr = bufio.NewScanner(os.Stdin), bufio.NewWriter(os.Stdout)

func scanString() string   { sc.Scan(); return sc.Text() }
func scanRunes() []rune    { return []rune(scanString()) }
func scanInt() int         { a, _ := strconv.Atoi(scanString()); return a }
func scanInt64() int64     { a, _ := strconv.ParseInt(scanString(), 10, 64); return a }
func scanFloat64() float64 { a, _ := strconv.ParseFloat(scanString(), 64); return a }

func scanInts(n int) []int {
	a := make([]int, n)
	for i := 0; i < n; i++ {
		a[i] = scanInt()
	}
	return a
}

func debug(a ...interface{}) {
	if os.Getenv("ONLINE_JUDGE") == "false" {
		fmt.Fprintln(os.Stderr, a...)
	}
}

func abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}

//•*¨*•.¸¸♪main•*¨*•.¸¸♪(　-ω-)ノ　(　・ω・)
func main() {
	defer wr.Flush()
	sc.Split(bufio.ScanWords)
	sc.Buffer(make([]byte, 10000), 1001001)

	n, q := scanInt(), scanInt()
	seg := newSegmentTree(n, func(a, b node) node {
		if a > b {
			return a
		}
		return b
	}, 0)

	for i := 0; i < n; i++ {
		seg.set(i, node(scanInt()))
	}
	seg.build()

	for i := 0; i < q; i++ {
		t := scanInt()
		if t == 1 {
			x, v := scanInt()-1, scanInt()
			seg.update(x, node(v))
		} else if t == 2 {
			l, r := scanInt()-1, scanInt()
			fmt.Fprintln(wr, seg.query(l, r))
		} else {
			x, a := scanInt()-1, scanInt()
			fmt.Fprintln(wr, seg.maxRight(x, func(v node) bool { return v < node(a) })+1)
		}
	}

}

type node int
type segmentTree struct {
	d  []node
	e  node
	n  int
	sz int
	op func(a, b node) node
}

func newSegmentTree(n int, f func(a, b node) node, e node) *segmentTree {
	sz := 1
	for sz < n {
		sz <<= 1
	}
	s := segmentTree{make([]node, 2*sz), e, n, sz, f}
	for i := 0; i < 2*sz; i++ {
		s.d[i] = e
	}
	return &s
}

func (s segmentTree) set(i int, a node) {
	s.d[i+s.sz] = a
}

func (s segmentTree) get(i int) node {
	return s.d[i+s.sz]
}

func (s segmentTree) build() {
	for i := s.sz - 1; i > 0; i-- {
		s.d[i] = s.op(s.d[2*i], s.d[2*i+1])
	}
}

func (s segmentTree) update(i int, a node) {
	i += s.sz
	s.d[i] = a
	for i >>= 1; i > 0; i >>= 1 {
		s.d[i] = s.op(s.d[2*i], s.d[2*i+1])
	}
}

func (s segmentTree) query(l, r int) node {
	vl, vr := s.e, s.e
	l += s.sz
	r += s.sz
	for l < r {
		if l&1 == 1 {
			vl = s.op(vl, s.d[l])
			l++
		}
		if r&1 == 1 {
			r--
			vr = s.op(s.d[r], vr)
		}
		l >>= 1
		r >>= 1
	}
	return s.op(vl, vr)
}

func (s segmentTree) maxRight(l int, f func(v node) bool) int {
	if l == s.n {
		return s.n
	}
	v := s.e
	l += s.sz
	for {
		for l&1 == 0 {
			l >>= 1
		}
		if !f(s.op(v, s.d[l])) {
			for l < s.sz {
				l <<= 1
				if f(s.op(v, s.d[l])) {
					v = s.op(v, s.d[l])
					l++
				}
			}
			return l - s.sz
		}
		v = s.op(v, s.d[l])
		l++
		if l&-l == l {
			break
		}
	}
	return s.n
}

func (s segmentTree) minLeft(r int, f func(v node) bool) int {
	if r == 0 {
		return 0
	}
	v := s.e
	r += s.sz
	for {
		r--
		for r > 1 && r&1 == 1 {
			r >>= 1
		}
		if !f(s.op(v, s.d[r])) {
			for r < s.sz {
				r = r<<1 + 1
				debug(r, f(s.op(v, s.d[r])))
				if f(s.op(v, s.d[r])) {
					v = s.op(v, s.d[r])
					r--
				}
			}
			return r + 1 - s.sz
		}
		v = s.op(v, s.d[r])
		if r&-r == r {
			break
		}
	}

	return 0
}


Measured memory_bytes: 10484