/// Arbori de intervale pentru maxim
#include <fstream>
#define NMAX 100001
using namespace std;
ifstream cin("arbint.in");
ofstream cout("arbint.out");
int array[NMAX], segment_tree[4*NMAX];
void build(int node, int left, int right) /// O(n)
{
if(left==right)
{
segment_tree[node]=array[left];
}
else
{
int middle=left+(right-left)/2; /// calculam mijlocul avand grija la overflow
build(node*2, left, middle);
build(node*2+1, middle+1, right);
/** vezi aici maxim*/
segment_tree[node]=max(segment_tree[node*2],
segment_tree[node*2+1]); /// maximul dintre fii sai
}
}
void update(int node, int left, int right, int position, int new_value)
{
if(left==right)
{
segment_tree[node]=new_value;
}
else
{
int middle=left+(right-left)/2;
if(position <= middle)
update(node*2, left, middle, position, new_value);
else
update(node*2+1, middle+1, right, position, new_value);
/** vezi aici maxim */
segment_tree[node]=max(segment_tree[node*2],
segment_tree[node*2+1]);
}
}
int query(int node, int left, int right, int query_left, int query_right)
{
if(query_left<=left && query_right>=right)
{
return segment_tree[node];
}
else
{
int middle=left+(right-left)/2;
if(query_right<=middle)
return query(node*2, left, middle, query_left, query_right);
if(query_left>=middle+1)
return query(node*2+1, middle+1, right, query_left, query_right);
/** vezi maxim aici */
return max(query(node*2, left, middle, query_left, query_right),
query(node*2+1, middle+1, right, query_left, query_right));
}
}
int main()
{
int N, M, p, left, right;
cin >> N >> M;
for(int i=1; i<=N; i++)
cin >> array[i];
build(1, 1, N);
while(M)
{
cin >> p >> left >> right;
if(p==1)
update(1, 1, N, left, right);
if(p==0)
cout << query(1, 1, N, left, right) << '\n';
M--;
}
return 0;
}