#include <fstream>
#include <vector>
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
struct Node
{
int value;
Node* left, * right;
Node(int v) : value(v), left(nullptr), right(nullptr) {}
};
void insertOrUpdateNode(int value, int position, int l, int& r, Node*& root)
{
if (position > r)
{
Node* newNode = new Node(value);
newNode->left = root;
root = newNode;
r *= 2;
insertOrUpdateNode(value, position, l, r, root);
return;
}
if (l == r && position == l)
{
root->value = value;
return;
}
int m = (l + r) / 2;
if (position <= m)
{
if (root->left == nullptr)
{
root->left = new Node(0);
}
insertOrUpdateNode(value, position, l, m, root->left);
}
else
{
if (root->right == nullptr)
{
root->right = new Node(0);
}
insertOrUpdateNode(value, position, m + 1, r, root->right);
}
root->value = std::max(root->left ? root->left->value : 0,
root->right ? root->right->value : 0);
}
int query(Node* root, int l, int r, int ql, int qr)
{
if (l > qr || r < ql || root == nullptr)
return 0; // Out of range or no node
if (ql <= l && r <= qr)
return root->value; // Current segment is completely within the query range
int m = (l + r) / 2;
int leftResult = 0;
int rightResult = 0;
if (qr > m)
{
rightResult = query(root->right, m + 1, r, ql, qr);
}
if (ql <= m)
{
leftResult = query(root->left, l, m, ql, qr);
}
return max(leftResult, rightResult);
}
int main()
{
ifstream fin("arbint.in");
ofstream fout("arbint.out");
if (!fin.is_open() || !fout.is_open()) {
return 1; // Error opening files
}
int capacity = 1;
int n, m; // number of queries
fin >> n >> m;
int x;
fin >> x;
Node* root = new Node(x); // Initialize the root of the tree
for (int i = 2; i <= n; ++i)
{
int x;
fin >> x;
insertOrUpdateNode(x, i, 1, capacity, root);
}
for (int i = 0; i < m; ++i)
{
int op, l, r;
fin >> op >> l >> r;
if (op == 1) // Update operation
{
insertOrUpdateNode(r, l, 1, capacity, root);
}
else if (op == 0) // Query operation
{
int result = query(root, 1, capacity, l, r);
fout << result << endl;
}
}
return 0;
}