///Arbori de intervale
#include <stdio.h>
#include <iostream>
using namespace std;
FILE *f,*g;
int arb[262200],N,X,Poz=1,M;///numărul de elemente trebuie să fie minimmul expresiei 2^k>=2N-1
/*int WTF()
{
int n=1;
while(n<2*100000)n*=2;
return n;
}*/
int MAX(int A,int B)
{
return (A>B ? A:B);
}
void BuildInlet(int st,int dr,int P)
{
int Middle;
if(st==dr)///am găsit poziția din arbore
{
arb[P]=X;
return;
}
Middle=(st+dr)/2;
if(Poz<=Middle)
BuildInlet(st,Middle,P*2);///jumătatea stângă
else BuildInlet(Middle+1,dr,P*2+1);///jumătatea dreaptă
arb[P]=MAX(arb[P*2],arb[P*2+1]);///începem să comparăm subordonații fiecărui nod, unind intervalele
}
void Read()
{
int i;
fscanf(f,"%d %d",&N,&M);
for(i=1;i<=N;i++,Poz=i)
{
fscanf(f,"%d",&X);
BuildInlet(1,N,1);
}
}
int A,B,NumberSearched;
void Search(int st,int dr,int P)
{
int Middle;
if(A<=st&&dr<=B)///am gasit un interval cuprins intre A si B
{
if(arb[P]>NumberSearched)NumberSearched=arb[P];///verific maximul din el
return;
}
Middle=(st+dr)/2;
if(A<=Middle)///daca A e mai in stanga decat mijlocul
Search(st,Middle,P*2);/// ma deplasez in stanga
if(B>Middle)
Search(Middle+1,dr,P*2+1);///altfel o iau in dreapta
}
void Solve()
{
int i,operation;
for(i=1;i<=M;i++)
{
fscanf(f,"%d %d %d",&operation,&A,&B);
if(!operation)
{
NumberSearched=0;
Search(1,N,1);
fprintf(g,"%d\n",NumberSearched);
}
else
Poz=A,X=B,BuildInlet(1,N,1);
}
}
int main()
{
f=fopen("arbint.in","r");g=fopen("arbint.out","w");
/*cout<<WTF();*/
Read();
Solve();
fclose(f),fclose(g);
return 0;
}