#include <stdio.h>
#include <stdlib.h>
//functie pentru a calcula maximul dintre doua numere intregi
int max (int a, int b) {
if(a < b) return b;
return a;
}
//functia pentru a construi arborele de intervale initial (indexarea se face de la 0)
void build (int *tree, int *array, int left, int right, int index) {
int mid = (left + right) / 2;
if(left == right) {
// setam valoarea frunzelor de la nivelul inferior la valoarea corespunzatoare din array
tree[index] = array[left];
}
else {
//daca exista un nivel inferior nodului curent, setam recursiv valoarea nodurilor inferioare, apoi ne intoarcem in acesta si ii setam valoarea drept maximul dintre fii sai;
build (tree, array, left, mid, index * 2 + 1);
build (tree, array, mid+1, right, index * 2 + 2);
tree[index] = max(tree[index * 2 + 1], tree[index * 2 + 2]);
}
}
//functie de retragere a valorii maxime din array pe intervalul [left,right]
int query (int *tree, int left, int right, int node_left, int node_right, int index) {
//daca intervalul curent se include complet in query, returnam valoarea nodului curent
if(index==-2)return 0;
if(node_left >= left && node_right <= right) {
return tree[index];
}
//verificam daca nodul curent nu e complet in afara query-ului, caz in care il aruncam;
else if(node_left > right || node_right < left) {
return 0;
}
//de altfel, apelam query pe fii nodului curent, pentru ca nu putem determina inca nimic, si returnam maximul dintre raspunsuri;
else {
int mid = (node_left + node_right) / 2;
return max (query (tree, left, right, node_left, mid, index * 2 + 1), query (tree, left, right, mid + 1, node_right, index * 2 + 2));
}
}
//functie ce face update pe nodul target, schimbandu-i valoarea in new_value
void update (int *tree, int target, int new_value, int left, int right, int index) {
int mid = (left + right) / 2;
//daca am ajuns in nodul target, modificam valoarea sa si finalizam recursia;
if(left == right) {
tree[index] = new_value;
return;
}
//alegem partea stanga sau drepta, in dependenta de unde se afla nodul target in arbore, si updatam fiul respectiv;
if(target <= mid) {
update (tree, target, new_value, left, mid, index * 2 + 1);
}
else {
update (tree, target, new_value, mid + 1, right, index * 2 + 2);
}
tree[index] = max (tree[index * 2 + 1], tree[index * 2 + 2]);
}
int main() {
//citim numarul de elemente, numarul de operatii si elementele;
freopen("arbint.in","r",stdin);
freopen("arbint.out","w",stdout);
int n, *array, q;
scanf ("%d%d", &n, &q);
array = (int*)malloc (n * sizeof (int));
for (int i = 0; i < n; i++) {
scanf ("%d", &array[i]);
}
//declaram si construim arborele de intervale
int *tree;
tree = (int*)malloc (4 * n * sizeof (int));
build (tree, array, 0, n, 0);
//rezolvam q queries
while (q--) {
int choice, a, b;
scanf ("%d%d%d", &choice, &a, &b);
a--;
b--;
if (!choice) {
printf ("%d\n", query (tree, a, b, 0, n, 0));
}
else {
update (tree, a, b, 0, n, 0);
}
}
}