// Arbori de intervale.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <bits/stdc++.h>
#define NMAX 100010
using namespace std;
ifstream f("arbint.in");
ofstream g("arbint.out");
vector<int> v;
int n, m, aint[4 * NMAX];
void build(int, int, int);
void update(int, int, int, int, int);
int query(int, int, int, int, int);
int main()
{
f >> n >> m;
v.push_back(0);
for (int i = 1; i <= n; i++) {
int x;
f >> x;
v.push_back(x);
}
build(1, 1, n);
while (m--) {
int op, a, b;
f >> op >> a >> b;
if (op == 0) g << query(1, 1, n, a, b) << '\n';
else update(1, 1, n, a, b);
}
}
void build(int nod, int st, int dr) {
if (st == dr) aint[nod] = v[st];
else {
int mid = (st + dr) / 2;
build(nod * 2, st, mid);
build(nod * 2 + 1, mid + 1, dr);
aint[nod] = max(aint[nod * 2], aint[nod * 2 + 1]);
}
}
void update(int nod, int st, int dr, int x, int y) {
if (st == dr) aint[nod] = y;
else {
int mid = (st + dr) / 2;
if (x <= mid) update(nod * 2, st, mid, x, y);
else update(nod * 2 + 1, mid + 1, dr, x, y);
aint[nod] = max(aint[nod * 2], aint[nod * 2 + 1]);
}
}
int query(int nod, int st, int dr, int x, int y) {
if (x <= st && dr <= y) return aint[nod];
else {
int ans = INT_MIN, mid = (st + dr) / 2;
if (x <= mid) ans = max(ans, query(2 * nod, st, mid, x, y));
if (y >= mid + 1) ans = max(ans, query(2 * nod + 1, mid + 1, dr, x, y));
return ans;
}
}