#include <fstream>
#include <vector>
#include <algorithm>
#include <limits>
std::ifstream f("arbint.in");
std::ofstream g("arbint.out");
enum OpType { MAX = 0, CHANGEVAL = 1 };
constexpr int MAX_S = 100001;
int n, numOp;
std::vector<int> v(MAX_S);
std::vector<int> segTree(4 * MAX_S, std::numeric_limits<int>::min());
void ConstructTree(int lo, int hi, int pos)
{
if (lo == hi) {
segTree[pos] = v[lo];
return;
}
int mid = lo + (hi - lo) / 2;
ConstructTree(lo, mid, 2 * pos + 1);
ConstructTree(mid + 1, hi, 2 * pos + 2);
segTree[pos] = std::max(segTree[2 * pos + 1], segTree[2 * pos + 2]);
}
int GetMaxFromInterval(int a, int b, int lo, int hi, int pos = 0)
{
if (a <= lo && b >= hi) // total overlap
return segTree[pos];
if (a > hi || b < lo) // no overlap
return std::numeric_limits<int>::min();
int mid = lo + (hi - lo) / 2;
return std::max(GetMaxFromInterval(a, b, lo, mid, 2 * pos + 1),
GetMaxFromInterval(a, b, mid + 1, hi, 2 * pos + 2));
}
void UpdateTree(int node, int lo, int hi, int pos, int val)
{
if (lo == hi) {
segTree[node] = val;
return;
}
int mid = lo + (hi - lo) / 2;
if (pos <= mid)
UpdateTree(2 * node + 1, lo, mid, pos, val);
if (pos > mid)
UpdateTree(2 * node + 2, mid + 1, hi, pos, val);
segTree[node] = std::max(segTree[2 * node + 1], segTree[2 * node + 2]);
}
void Read()
{
f >> n >> numOp;
int i, type, a, b;
for (i = 0; i < n; ++i) {
f >> v[i];
}
ConstructTree(0, n - 1, 0);
for (i = 0; i < numOp; ++i) {
f >> type >> a >> b;
switch (type)
{
case OpType::MAX: {
g << GetMaxFromInterval(a - 1, b - 1, 0, n - 1) << '\n';
break;
}
case OpType::CHANGEVAL: {
v[a - 1] = b;
UpdateTree(0, 0, n - 1, a - 1, b);
break;
}
}
}
}
int main(int, char *[])
{
Read();
return 0;
}