#include <iostream>
#include <fstream>
#define Nmax 100010
#include <cmath>
using namespace std;
ifstream fin("arbint.in");
ofstream fout("arbint.out");
int n,v[Nmax],m;
int arb[300000];
void Citire ()
{ int i;
fin>>n>>m;
for (i=1; i<=n; i++)
fin>>v[i];
}
int Creare_arb (int l,int r,int nod)
{ int v1,v2;
if (r==l) arb[nod]=v[l]; //daca sunt intr o frunza maximul pe intervalul ala e chiar elementul din vector
else
{
int middle=(l+r)/2;
Creare_arb ( l,middle,2*nod ) ; //impart intervalul in 2
Creare_arb ( middle+1 ,r , 2*nod+1 ) ;
arb[nod]=max(arb[2*nod],arb[2*nod+1]); //altfel, e maximul dintre cele 2 intervale de sub el
}
}
int cautare_binara (int nod,int st,int dr,int x) // caut frunza (adica pozitia in vectorul arb ) corespunzatoare pozitiei x din vectorul inital
{
while (st<=dr)
{ int middle=(st+dr)/2;
if (st==dr&&st==x) return nod;
if (middle<x) { nod = nod*2 + 1 ; st = middle + 1 ; }
else { nod = nod*2 ; dr = middle ; }
}
}
void update (int poz)
{
arb[poz/2]=max(arb[poz],arb[poz+1]);
if (poz!=1) update (poz/2);
}
int query (int nod,int l,int r,int st,int dr)
{ int v1=0,v2=0;
if (l>=st && r<=dr)
return arb[nod];
if (l>dr||r<st) return 0;
int middle=l+(r-l)/2;
v1=query (2*nod,l,middle,st,dr);
v2=query (2*nod+1,middle+1,r,st,dr);
return max(v1,v2);
}
int main()
{int i,x,y,z;
Citire();
Creare_arb(1,n,1);
for (i=1;i<=m;i++)
{
fin>>x>>y>>z;
if (x==0) fout<<query(1,1,n,y,z)<<"\n";
if (x==1)
{
v[y]=z;
int poz=cautare_binara(1,1,n,y);
arb[poz]=z;
update(poz);
}
}
return 0;
}