#include <algorithm>
#include <stdio.h>
#define MAXN 100000
int Arb[1 << 18]; //Alegem k astfel incat 2^k>=2*N-1.
inline
void Update(
int nod, //Nod curent.
int st, //Stanga.
int dr, //Dreapta.
int poz, //Pozitia de inserare a noului element.
int val //Valoarea cu care va fi actualizat elementul de pe poz.
)
{
//Interval elementar.
if ( st == poz && poz == dr )
{
Arb[ nod ] = val;
return; //Ma opresc deoarece nu mai am fii si nu mai este necesar sa actualizez maximul dintre ei.
}
//Calculez mijlocul.
int m = (st + dr) >> 1;
//Daca pozitia se afla in stanga mijlocului.
if ( poz <= m ) Update((nod << 1), st, m, poz, val); //Fiu stanga.
else Update((nod << 1) + 1, m + 1, dr, poz, val); //Fiu dreapta.
//Actaulizez maximul dintre cei doi fii.
Arb[ nod ] = std::max(Arb[ (nod << 1) ], Arb[ (nod << 1) + 1 ]);
}
/*
* Returneaza maximul din intervalul [a,b].
*/
inline
int Query(
int nod, //Nodul curent.
int st, //Stanga.
int dr, //Dreapta.
int a, //Capatul din stanga al intervalului cautat.
int b //Capatul din dreapta al intervalului cautat.
)
{
//Intervalul [st,dr] este inclus in [a,b].
if (a <= st && dr <= b)
{
return Arb[ nod ];
}
//Initial sunt 0.
int Qst = 0; //Query pentru fiul stang.
int Qdr = 0; //Query pentru fiul drept.
//Calculez mijlocul.
int m = (st + dr) >> 1;
//Daca intervalul cautat contine valori in fiul stang.
if (a <= m)
Qst = Query((nod << 1), st, m, a, b);
//Daca intervalul cautat contine valori in fiul drept.
if (b > m)
Qdr = Query((nod << 1) + 1, m + 1, dr, a, b);
return std::max(Qst, Qdr);
}
int main()
{
int N, M;
int cer, a, b;
FILE * f = fopen("arbint.in", "r");
FILE * g = fopen("arbint.out", "w");
if (f && g)
{
fscanf(f, "%d %d", &N, &M);
for (int i = 1, x; i <= N; i++)
{
fscanf(f, "%d", &x);
Update(1, 1, N, i, x);
}
for (int i = 1; i <= M; i++)
{
fscanf(f, "%d %d %d", &cer, &a, &b);
if (cer)
Update(1, 1, N, a, b);
else
fprintf(g, "%d\n", Query(1, 1, N, a, b) );
}
fclose(f);
fclose(g);
}
return 0;
}