// http://www.infoarena.ro/problema/arbint
//#define DEBUG 1
#include <fstream>
#ifndef __SEGTREE__H__
#define __SEGTREE__H__
#include <assert.h>
#include <cmath>
#include <iostream>
#include <queue>
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define OP(a,b) ((a) > (b) ? (a) : (b))
#define LEFT(i) (2*(i)+1)
#define RIGHT(i) (2*(i)+2)
#define PARENT(i) ((i-1)/2)
using namespace std;
template <typename T>
struct Node {
// indices
int left, right;
// cached results of subtrees
T value;
Node() : left(-1), right(-1), value(T()) {}
Node(int left, int right, T value): left(left), right(right), value(value) {}
friend ostream& operator<<(ostream& out, Node& n) {
out << "[" << n.left << ", " << n.right << "], op = " << n.value << "\n";
return out;
}
};
template <typename T>
class SegTree {
Node<T> *elems;
int n;
int input_size;
public:
SegTree(T *input, int input_size) {
this->input_size = input_size;
int height = ceil(log2(this->input_size));
n = pow(2, height+1);
elems = new Node<T>[n];
build(0, 0, input_size - 1, input);
}
~SegTree() {
delete[] elems;
}
T query(int start, int end) {
#ifdef DEBUG
cout << "Query(" << start <<"," << end << ")\n";
#endif
return do_query(start, end, 0);
}
void update(int index, T new_val) {
if (index < 0 || index >= input_size)
return;
#ifdef DEBUG
cout << "update tree[" << index << "] = " << new_val << "\n";
#endif
int i = 0;
for(;;) {
if (elems[i].left == index)
break;
if (elems[LEFT(i)].right >= index)
i = LEFT(i);
else
i = RIGHT(i);
}
#ifdef DEBUG
cout << "Found position " << i << "for index " << index << "\n";
#endif
// go to leaf node that has [index, index]
while (elems[i].left != elems[i].right)
i = LEFT(i);
assert(elems[i].left == index);
elems[i].value = new_val;
while (i) {
elems[PARENT(i)].value = OP(
elems[LEFT(PARENT(i))].value,
elems[RIGHT(PARENT(i))].value
);
i = PARENT(i);
}
}
void update_recursive(int index, T new_val) {
#ifdef DEBUG
cout << "update tree[" << index << "] = " << new_val << "\n";
#endif
_update_recursive(0, 0, input_size-1, index, new_val);
#ifdef DEBUG
cout << *this;
#endif
}
friend ostream& operator<<(ostream &out, const SegTree& t) {
// do BFS on tree to print it in expected order.
queue<int> Q;
Q.push(0);
while (!Q.empty()) {
int index = Q.front();
Q.pop();
Node<T> n = t.elems[index];
out << index << " - " << n << " ";
if (n.left != n.right) {
Q.push(LEFT(index));
Q.push(RIGHT(index));
}
}
out << "\n";
return out;
}
private:
void _update_recursive(int index, int start, int end, int pos, T new_val) {
assert(start <= end);
if (start == end) {
#ifdef DEBUG
cout << "Found position " << pos << " for index " << index << "\n";
#endif
assert(start == pos);
elems[index].value = new_val;
return;
}
int mid = (start + end) / 2;
if (pos <= mid)
_update_recursive(LEFT(index), start, mid, pos, new_val);
else
_update_recursive(RIGHT(index), mid+1, end, pos, new_val);
#ifdef DEBUG
cout << "Updating " << index << " with " << LEFT(index) << ", " << RIGHT(index) << "\n";
#endif
elems[index].value = OP(
elems[LEFT(index)].value,
elems[RIGHT(index)].value
);
}
T do_query(int start, int end, int i) {
start = MAX(start, elems[i].left);
end = MIN(end, elems[i].right);
assert(start <= end);
if (start == elems[i].left && end == elems[i].right)
return elems[i].value;
if (end <= elems[LEFT(i)].right)
return do_query(start, end, LEFT(i));
else if (start >= elems[RIGHT(i)].left)
return do_query(start, end, RIGHT(i));
else
return OP(
do_query(start, end, LEFT(i)),
do_query(start, end, RIGHT(i))
);
}
void build(int idx, int left, int right, T *input) {
assert (left <= right);
T value;
if (left == right) {
value = input[left];
} else {
int mid = (right+left)/2;
build(LEFT(idx), left, mid, input);
build(RIGHT(idx), mid+1, right, input);
value = OP(elems[LEFT(idx)].value,
elems[RIGHT(idx)].value);
}
Node<T> x(left, right, value);
elems[idx] = x;
}
};
#endif
using namespace std;
int main() {
int n, m, op, a, b;
ifstream infile("arbint.in");
ofstream outfile("arbint.out");
infile >> n >> m;
#ifdef DEBUG
cout << n << " " << m << "\n";
#endif
int A[n];
for (int i = 0; i < n; ++i)
infile >> A[i];
SegTree<int> tree(A, n);
#ifdef DEBUG
cout << tree;
#endif
for (int i = 0; i < m; ++i) {
infile >> op >> a >> b;
if (op == 0) {
a--; b--;
int o = tree.query(a, b);
#ifdef DEBUG
cout << o << "\n";
#endif
outfile << o << "\n";
} else if (op == 1) {
a--;
tree.update_recursive(a, b);
} else {
#ifdef DEBUG
cout << "Unknown operation " << op << "\n";
#endif
}
}
return 0;
}