#include <bits/stdc++.h>
using namespace std;
vector<int> segm_tree;
ifstream fin("arbint.in");
ofstream fout("arbint.out");
void init(int l, int r, int node)
{
if (l == r) {
fin >> segm_tree[node];
return;
}
int mid = (l + r) / 2;
int left = node * 2, right = left + 1;
init(l, mid, left);
init(mid + 1, r, right);
segm_tree[node] = max(segm_tree[left], segm_tree[right]); /* pasul de procesare, in cazul asta gaseste maximul din segment */
}
void update(int pos, int val, int l, int r, int node)
{
if (l == r) {
segm_tree[node] = val;
}
int mid = (l + r) / 2; /* indexul de mijloc al segmentului curent */
int left = node * 2, right = left + 1; /* indexii din vectorul segm_tree */
if (pos <= mid)
update(pos, val, l, mid, node);
else
update(pos, val, mid+1, r, node);
segm_tree[node] = max(segm_tree[left], segm_tree[right]); /* cand urc vreau sa updatez max-urile */
}
int query(int ql, int qr, int l, int r, int node) /* va returna maximul pe segmentul (ql, qr) */
{
if (l <= ql && qr <= r)
return segm_tree[node];
int mid = (l+r)/2;
int left = node * 2, right = left + 1;
int left_result = -1, right_result = -1;
if (l <= ql && ql <= mid)
left_result = query(ql, qr, l, mid, left);
if (mid+1 <= qr && qr <= r)
right_result = query(ql, qr, mid+1, r, right);
return max(left_result, right_result);
}
int main()
{
int n, m;
fin >> n >> m;
segm_tree.resize(n << 2);
while (m--) {
int c, a, b;
fin >> c >> a >> b;
if (c == 0)
fout << query(a, b, 1, n, 1) << endl;
else if (c == 1)
update(a, b, 1, n, 1);
}
}