#include <fstream>
#include <iostream>
#include <math.h>
#include <vector>
#include <algorithm>
using namespace std;
class SegmentTree
{
public:
vector <int> tree;
SegmentTree(const vector <int>& values)
{
int log = log2(values.size());
m_size = log + ((1 << log) < values.size());
m_size = 1 << m_size;
tree.resize(m_size * 2);
Build(values, 0, m_size - 1, 1);
}
~SegmentTree()
{
}
int GetSize()
{
return m_size;
}
void Update(int val, int pos, int lf, int rg, int node)
{
if (lf == rg)
{
tree[node] = val;
return;
}
int mid = (lf + rg) / 2;
if (mid >= pos)
{
Update(val, pos, lf, mid, LfSon(node));
}
else
{
Update(val, pos, mid + 1, rg, RgSon(node));
}
tree[node] = max(tree[LfSon(node)], tree[RgSon(node)]);
}
int Query(int qLf, int qRg, int lf, int rg, int node)
{
if (qLf <= lf && qRg >= rg)
{
return tree[node];
}
int mid = (lf + rg) / 2;
int ans1 = 0, ans2 = 0;
if (mid >= qLf)
{
ans1 = Query(qLf, qRg, lf, mid, LfSon(node));
}
if (mid < qRg)
{
ans2 = Query(qLf, qRg, mid + 1, rg, RgSon(node));
}
return max(ans1, ans2);
}
private:
int m_size;
void Build(const vector <int>& values, int lf, int rg, int node)
{
if (lf == rg)
{
if (lf < values.size())
{
tree[node] = values[lf];
}
return;
}
int mid = (lf + rg) / 2;
Build(values, lf, mid, LfSon(node));
Build(values, mid + 1, rg, RgSon(node));
tree[node] = max(tree[LfSon(node)], tree[RgSon(node)]);
}
int LfSon(int node)
{
return node * 2;
}
int RgSon(int node)
{
return node * 2 + 1;
}
};
ifstream fin("arbint.in");
ofstream fout("arbint.out");
vector <int> v;
int main()
{
int n, m;
fin >> n >> m;
for (int i = 0; i < n; i++)
{
int x;
fin >> x;
v.push_back(x);
}
SegmentTree segmentTree(v);
for (int i = 0; i < m; i++)
{
int a, b, c;
fin >> a >> b >> c;
b--;
if (a == 1)
{
segmentTree.Update(c, b, 0, segmentTree.GetSize() - 1, 1);
}
else
{
c--;
fout << segmentTree.Query(b, c, 0, segmentTree.GetSize() - 1, 1) << '\n';
}
}
}