#define MAX_N 100000
#define LOG_MAX_N 16
#define INF 0x3f3f3f3f
#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;
ifstream fin("arbint.in");
ofstream fout("arbint.out");
struct nodeinfo
{
nodeinfo() :
a(0),
b(0),
ma(0),
u(0),
v(0),
f(0)
{
}
int a, b, ma, u, v, f;
};
int n, m, node_val, A[MAX_N + 1], I[MAX_N + 1];
nodeinfo G[1 << (LOG_MAX_N + 2)];
void ConstructTree(int node, int a, int b);
void Set(int index, int val);
void Update(int node);
int MaxRange(int a, int b);
int Query(int node, int b);
int Down(int node, int b);
int main()
{
fin >> n >> m;
for (int i = 1; i <= n; ++i)
{
fin >> A[i];
}
ConstructTree(++node_val, 1, n);
for (int i = 0, q, a, b; i < m; ++i)
{
fin >> q >> a >> b;
if (q == 0)
{
fout << MaxRange(a, b) << '\n';
}
else
{
Set(a, b);
}
}
fin.close();
fout.close();
return 0;
}
void ConstructTree(int node, int a, int b)
{
G[node].a = a;
G[node].b = b;
if (a == b)
{
I[a] = node;
G[node].ma = A[a];
}
else
{
const int mid = (a + b) / 2;
G[node].u = ++node_val;
G[node_val].f = node;
ConstructTree(node_val, a, mid);
G[node].v = ++node_val;
G[node_val].f = node;
ConstructTree(node_val, mid + 1, b);
G[node].ma = max(G[G[node].u].ma, G[G[node].v].ma);
}
}
void Set(int index, int val)
{
G[I[index]].ma = val;
Update(G[I[index]].f);
}
void Update(int node)
{
int ma_old;
while (node != 0)
{
ma_old = G[node].ma;
G[node].ma = max(G[G[node].u].ma, G[G[node].v].ma);
node = (G[node].ma != ma_old) ? G[node].f : 0;
}
}
int MaxRange(int a, int b)
{
return Query(I[a], b);
}
int Query(int node, int b)
{
if (G[node].b >= b)
{
return Down(node, b);
}
if (G[G[node].f].a < G[node].a)
{
return max(G[node].ma, Query(G[G[G[node].f].f].v, b));
}
else
{
return Query(G[node].f, b);
}
}
int Down(int node, int b)
{
if (G[node].b == b)
{
return G[node].ma;
}
if (G[G[node].u].b >= b)
{
return Down(G[node].u, b);
}
else
{
return Down(G[node].v, b);
}
}