#include <stdio.h>
#define MAXN 100005
int n, m, v[MAXN], arb[4 * MAXN];
// construim arborele: fiecare nod retine maximul din intervalul sau
void build(int nod, int st, int dr)
{
// daca am ajuns la o frunza, punem valoarea din vector
if (st == dr)
{
arb[nod] = v[st];
return;
}
// impartim intervalul in doua jumatati
int mij = (st + dr) / 2;
// construim prima jumatate (stanga)
build(2*nod, st, mij);
// construim a doua jumatate (dreapta)
build(2*nod+1, mij+1, dr);
// maximul din nodul curent e maximul dintre cei doi fii
arb[nod] = arb[2*nod] > arb[2*nod+1] ? arb[2*nod] : arb[2*nod+1];
}
// returnam maximul din intervalul [a, b]
int query(int nod, int st, int dr, int a, int b)
{
// daca intervalul nodului e complet inclus in [a,b], returnam direct
if (a <= st && dr <= b)
return arb[nod];
// altfel impartim in stanga si dreapta
int mij = (st + dr) / 2, rez = 0, x;
// mergem in stanga doar daca intervalul [a,b] se suprapune cu stanga
if (a <= mij)
{
x = query(2*nod, st, mij, a, b);
// retinem maximul gasit pana acum
if (x > rez) rez = x;
}
// mergem in dreapta doar daca intervalul [a,b] se suprapune cu dreapta
if (b > mij)
{
x = query(2*nod+1, mij+1, dr, a, b);
// retinem maximul gasit pana acum
if (x > rez) rez = x;
}
return rez;
}
// modificam valoarea de pe pozitia poz cu val, si refacem maximele pe drum
void update(int nod, int st, int dr, int poz, int val)
{
// am ajuns la frunza, modificam valoarea
if (st == dr)
{
arb[nod] = val;
return;
}
// impartim intervalul in doua jumatati
int mij = (st + dr) / 2;
// daca pozitia e in jumatatea stanga, mergem acolo
if (poz <= mij)
update(2*nod, st, mij, poz, val);
// altfel mergem in jumatatea dreapta
else
update(2*nod+1, mij+1, dr, poz, val);
// dupa ce am modificat jos, refacem maximul pentru nodul curent
arb[nod] = arb[2*nod] > arb[2*nod+1] ? arb[2*nod] : arb[2*nod+1];
}
int main()
{
FILE *fin = fopen("arbint.in", "r");
FILE *fout = fopen("arbint.out", "w");
fscanf(fin, "%d %d", &n, &m);
for (int i = 1; i <= n; i++)
fscanf(fin, "%d", &v[i]);
build(1, 1, n); // construim arborele pentru tot vectorul
for (int i = 0; i < m; i++)
{
int tip, a, b;
fscanf(fin, "%d %d %d", &tip, &a, &b);
if (tip == 0)
fprintf(fout, "%d\n", query(1, 1, n, a, b)); // tip 0 = query
else
update(1, 1, n, a, b); // tip 1 = update
}
fclose(fin);
fclose(fout);
return 0;
}