#include <fstream>
//@TODO: find proper size of stree[]
unsigned int *vect, *stree, N;
#define LEFT(node) ((node<<1)+1)
#define RIGHT(node) ((node<<1)+2)
void init (unsigned int node, unsigned int l, unsigned int r);
int queryMax (unsigned int node, unsigned int l, unsigned int r,
unsigned int ql, unsigned int qr);
void update (unsigned int node, unsigned int l, unsigned int r,
unsigned int upd, unsigned int to);
int main ()
{
std::ifstream f_in("arbint.in");
std::ofstream f_out("arbint.out");
unsigned int M, i, op, a, b;
f_in >> N >> M;
for (i = 0; i < N; ++i) f_in >> vect[i];
vect = new unsigned int[N];
stree = new unsigned int[(N<<1)+1];
init(0, 0, N-1);
for (i = 0; i < M; ++i) {
f_in >> op >> a >> b;
if (!op) {
--a; --b;
f_out << queryMax(0, 0, N-1, a, b) << '\n';
}
else {
--a;
update(0, 0, N-1, a, b);
}
}
f_in.close();
f_out.close();
return 0;
}
void init(unsigned int node, unsigned int l, unsigned int r)
{
if (l == r) stree[node] = vect[l];
else {
unsigned int mid = (l+r)>>1;
init(LEFT(node), l, mid);
init(RIGHT(node), mid+1, r);
if (stree[LEFT(node)] >= stree[RIGHT(node)])
stree[node] = stree[LEFT(node)];
else
stree[node] = stree[RIGHT(node)];
}
}
int queryMax (unsigned int node, unsigned int l, unsigned int r,
unsigned int ql, unsigned int qr)
{
if (r < ql || l > qr) return -1;
if (l >= ql && r <= qr) return stree[node];
unsigned int mid = (l+r)>>1;
int q1 = queryMax(LEFT(node) , l , mid, ql, qr),
q2 = queryMax(RIGHT(node), mid+1, r , ql, qr);
return q1 > q2 ? q1 : q2;
}
void update (unsigned int node, unsigned int l, unsigned int r,
unsigned int upd, unsigned int to)
{
if (upd < l || upd > r) return;
if (l == r) stree[node] = to;
else {
unsigned int mid = (l+r)>>1;
if (upd <= mid) update(LEFT(node), l, mid, upd, to);
else update(RIGHT(node), mid+1, r, upd, to);
if (stree[LEFT(node)] >= stree[RIGHT(node)])
stree[node] = stree[LEFT(node)];
else
stree[node] = stree[RIGHT(node)];
}
}