#include <fstream>
#include <vector>
using namespace std;
ifstream cin ("arbint.in");ofstream cout ("arbint.out");
vector<int> v;
vector<int> arb;
// pozitia nodului actual , st si dr intervalului corespunzator nodului ,
// pozitia pe care inserez , valoarea inserata
// Complexitate O(logN)
void update(int nod , int st , int dr , int poz , int val){
// cand ajung la o frunza ma opresc
if (st == dr){
arb[nod] = val;
return;
}
// iau mijlocul
int mij = (st + dr) / 2;
// verific pe ce fiu se afla pozitia
if (poz <= mij){
update (nod*2 , st , mij , poz , val);
}
else{
update (nod*2+1 , mij+1 , dr , poz , val);
}
// recalculez nodul
arb[nod] = max(arb[nod*2] , arb[nod*2+1]);
}
// pozitia nodului actual , st si dr intervalului corespunzator nodului ,
// ST si DR intervalului pe care vrem maxim
// Complexitate O(logN)
int query (int nod , int st , int dr , int ST , int DR){
// cand intervalul nodului este cuprins in intervalul pe care vrem maxim
if (st >= ST && dr <= DR){
return arb[nod];
}
// iau mijlocul
int mij = (st + dr) / 2;
int MAX = 0;
if (ST <= mij){ // verific daca fiul stang contine macar o parte din intervalul cautat
MAX = max(MAX , query (nod*2 , st , mij , ST , DR));
}
if (mij < DR){ // verific daca fiul drept contine macar o parte din intervalul cautat
MAX = max(MAX , query (nod*2+1 , mij+1 , dr , ST , DR));
}
return MAX;
}
int main() {
// nr pozitii , nr intrebari
int n , m;
cin>>n>>m;
// redimensionari
v.resize(n+1);
arb.resize(4*n);
// elementele initiale
for (int i=1; i<=n; i++){
cin>>v[i];
update (1 , 1 , n , i , v[i]);
}
// intrebari
for (int i=1; i<=m; i++){
int t , a , b;
cin>>t>>a>>b;
if (t == 1){
update (1 , 1 , n , a , b);
}
else{
cout<<query(1 , 1 , n , a , b)<<'\n';
}
}
return 0;
}