#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);
int MaxRange(int a, int b);
void Query(int node, int a, int b, int& ma);
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;
int node = G[I[index]].f;
while (node != 0)
{
G[node].ma = max(G[G[node].u].ma, G[G[node].v].ma);
node = G[node].f;
}
}
int MaxRange(int a, int b)
{
int ma = G[I[a]].ma;
Query(I[a], a, b, ma);
return ma;
}
void Query(int node, int a, int b, int& ma)
{
if (G[node].a >= a && G[node].b <= b)
{
ma = max(ma, G[node].ma);
}
if (G[node].b < b)
{
if (G[G[node].v].a >= a)
{
ma = max(ma, G[G[node].v].ma);
}
Query(G[node].f, a, b, ma);
}
if (G[node].b >= b)
{
if (G[node].a >= a && G[node].b == b)
{
return;
}
if (G[G[node].u].b >= b)
{
Query(G[node].u, a, b, ma);
}
else
{
if (G[G[node].u].a >= a)
{
ma = max(ma, G[G[node].u].ma);
}
Query(G[node].v, a, b, ma);
}
}
}