#include <fstream>
#include <algorithm>
using namespace std;
#define NMAX 1 << 19
#define L(x) ( (x) << 1 ) //stanga unui nod
#define R(x) ( (x) << 1 ) + 1 //dreapta unui nod
ifstream fin("arbint.in");
ofstream fout("arbint.out");
int Aint[NMAX];
// update pe pozitie
void Update(int nod, int st, int dr, int poz, int val) {
// daca sunt pe elementul dorit, il actualizez
if (st >= poz && dr <= poz) {
Aint[nod] = val;
return;
}
int mid = (st + dr) >> 1;
//daca elementul meu e in stanga
if (poz <= mid)
Update(L(nod), st, mid, poz, val);
else // altfel pe dreapta
Update(R(nod), mid + 1, dr, poz, val);
Aint[nod] = max(Aint[L(nod)], Aint[R(nod)]);
}
// queery pe interval
int Query(int nod, int st, int dr, int a, int b) {
// daca intervalul din aint e inclus total in intervalul cautat returnez informatia
if (a <= st && dr <= b)
return Aint[nod];
int mid = (st + dr) >> 1;
int t1 = -1, t2 = -1;
// daca intervalul meu are o parte in stanga, apelez
if (a <= mid)
t1 = Query(L(nod), st, mid, a, b);
if (b > mid) //daca are o parte in dreapta, apelez
t2 = Query(R(nod), mid + 1, dr, a, b);
return max(t1, t2);
}
int main() {
int N, M, x, y, t;
// citire si creeare arbore
fin >> N >> M;
for (int i = 1; i <= N; ++i) {
fin >> x;
Update(1, 1, N, i, x);
}
// rezolvare
while (M--) {
fin >> t >> x >> y;
if (t == 0)
fin >> Query(1, 1, N, x, y);
else
Update(1, 1, N, x, y);
}
return 0;
}