#include <fstream>
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
#define inf INT_MIN
using namespace std;
int segm_tree[270000];
int a[100002];
int cont;
void build (int node, int left, int right) { // node - indexul segm_tree, st / dr - capetele segmentului curent
cont++;
if (left == right) {
segm_tree[node] = a[left]; // nod frunza
} else {
int middle = (left + right) / 2;
build (2 * node + 1, left, middle); // apeleaza recursiv ambele jumatati
build (2 * node + 2, middle +1, right);
segm_tree[node] = max(segm_tree[2 * node + 1], segm_tree[2 * node + 2]); // recalculare
}
}
void update (int node, int left, int right, int x, int y) {
if (left == right) { // suntem in nodul x (Frunza)
segm_tree[node] = y;
} else {
int middle = (left + right) / 2;
if (x <= middle) { // actualizam fiul din stanga
update(2 * node +1, left, middle, x, y);
} else {
update(2 * node +2, middle + 1, right, x, y); // actualizam fiul din dreapta
}
segm_tree[node] = max(segm_tree[2 * node + 1], segm_tree[2 * node + 2]); // recalculare
}
}
int query (int node, int left, int right, int x, int y) {
if (x <= left && right <= y) { // segmentul nodului e complet inclus in query
return segm_tree[node];
} else {
int answer = inf;
int middle = (left + right) / 2;
if (x <= middle) { // fiul din stanga si segmentul din query au cel putin un element in comun
answer = max(answer, query(2 * node + 1, left, middle, x, y));
}
if (y >= middle + 1) { // fiul din dreapta si segmentul din query au cel putin un element in comun
answer = max(answer,query(2 * node + 2, middle + 1, right, x, y));
}
return answer;
}
}
int main()
{
ifstream f("arbint.in");
ofstream g("arbint.out");
int n, m, i, x, y, z;
f >> n >> m;
for (i=1; i<=n; ++i)
f >> a[i];
build(0,1,n);
for (i=1; i<=m; ++i)
{
f >> x >> y >> z;
if (x) update(0,1,n,y,z);
else g << query(0,1,n,y,z) <<"\n";
}
return 0;
}