#include <stdio.h>
#define NMAX 100005
int n, m;
int arb[4 * NMAX]; // arborele de intervale
int a[NMAX];
int max2(int x, int y) { return x > y ? x : y; }
// Construieste arborele din vectorul a[]
void build(int node, int l, int r) {
if (l == r) {
arb[node] = a[l]; // nod frunza
return;
}
int mid = (l + r) / 2;
build(2 * node, l, mid);
build(2 * node + 1, mid + 1, r);
arb[node] = max2(arb[2 * node], arb[2 * node + 1]); // max din fii
}
// Actualizeaza pozitia pos cu valoarea val
void update(int node, int l, int r, int pos, int val) {
if (l == r) {
arb[node] = val;
return;
}
int mid = (l + r) / 2;
if (pos <= mid) update(2 * node, l, mid, pos, val);
else update(2 * node + 1, mid + 1, r, pos, val);
arb[node] = max2(arb[2 * node], arb[2 * node + 1]); // reface maximul pe drum inapoi
}
// Returneaza maximul din intervalul [ql, qr]
int query(int node, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr)
return arb[node]; // intervalul e complet inclus in query
int mid = (l + r) / 2;
int ans = 0;
if (ql <= mid) ans = max2(ans, query(2 * node, l, mid, ql, qr));
if (qr > mid) ans = max2(ans, query(2 * node + 1, mid + 1, r, ql, qr));
return ans;
}
int main() {
freopen("arbint.in", "r", stdin);
freopen("arbint.out", "w", stdout);
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
build(1, 1, n);
for (int i = 0; i < m; i++) {
int tip, x, y;
scanf("%d %d %d", &tip, &x, &y);
if (tip == 0) printf("%d\n", query(1, 1, n, x, y)); // query max
else update(1, 1, n, x, y); // update pozitie
}
return 0;
}