#include <fstream>
#include <iostream>
using namespace std ;
ifstream f ("arbint.in") ;
ofstream g ("arbint.out") ;
int N , M , V[100005] , Arb_int[4*100000+5] ;
void creare_arb ( int indice_arbore_curent , int st , int dr )
{
if ( st == dr ) // am gasit frunza corespunzatoare pozitiei st din vector
Arb_int[indice_arbore_curent] = V[st] ; // atribuim
else
{
//altfel suntem la o reuniune de intervale
int m = ( st + dr ) / 2 ; //aflam mijlocul dupa care impartim
creare_arb ( 2 * indice_arbore_curent , st , m ) ; //apelam de fiul stang
creare_arb ( 2 * indice_arbore_curent + 1 , m + 1 , dr ) ; //apelam de fiul drept
Arb_int[indice_arbore_curent] = max ( Arb_int[2*indice_arbore_curent] , Arb_int[2*indice_arbore_curent+1] ) ; //alegem valoarea maxima dintre cei doi subarbori
}
}
void update ( int i , int val , int indice_arbore_curent , int st , int dr )
{
if ( st == dr ) //am ajuns in frunza unde inlocuim
Arb_int[indice_arbore_curent] = val ;
else //momentan suntem intr-un subarbore care contine frunza noastra
{
//mai intai reapelam de un subarbore stang/drept sa ajungem la frunza , iar la intoarcere updatam lucrurile
int m = ( st + dr ) / 2 ;
if ( i <= m ) //se afla in subarborele stang
update ( i , val , 2 * indice_arbore_curent , st , m ) ;
else //altfel este in cel drept
update ( i , val , 2 * indice_arbore_curent + 1 , m + 1 , dr ) ;
Arb_int[indice_arbore_curent] = max ( Arb_int[2*indice_arbore_curent] , Arb_int[2*indice_arbore_curent+1] ) ; //alegem valoare maxima din cei doi subarbori
}
}
int query ( int i , int j , int indice_arbore_curent , int st , int dr )
{
if ( i == st && j == dr ) //am ajuns intr-o celula in care este deja memorat raspunsul unui interval
return Arb_int[indice_arbore_curent] ;
int m = ( st + dr ) / 2 ;
if ( j <= m ) //intervalul se afla total in subarborele stang
return query ( i , j , 2 * indice_arbore_curent , st , m ) ;
if ( m < i ) //intervalul se afla total in subarborele drept
return query ( i , j , 2 * indice_arbore_curent + 1 , m + 1 , dr ) ;
//daca nu s-a indeplinit niciuna pana acum ... inseamna ca ne impartim in ambii subarbori
return max ( query ( i , m , 2 * indice_arbore_curent , st , m ) , query ( m + 1 , j , 2 * indice_arbore_curent + 1 , m + 1 , dr ) ) ;
}
int main ()
{
f >> N >> M ;
for ( int i = 1 ; i <= N ; ++i )
f >> V[i] ;
creare_arb ( 1 , 1 , N ) ;
for ( int i = 1 ; i <= M ; ++i )
{
int tip , a , b ;
f >> tip >> a >> b ;
if ( tip == 0 )
g << query ( a , b , 1 , 1 , N ) << "\n" ;
else
update ( a , b , 1 , 1 , N ) ;
}
}