#include <fstream>
#include <iostream>
#include <vector>
#define Nmax 100100
#define Tmax 400400
#define son Tree[node][i]
using namespace std;
struct {
int value, father, level, sons, stripeId, position;
} Data[Nmax];
vector <int> Tree[Nmax], Stripe[Nmax];
int N, countStripes;
class segmentTrees {
#define root 1
#define leftSon (node << 1)
#define rightSon ((node << 1) | 1)
#define middle ((left + right) >> 1)
private:
int top, tree[Tmax], start[Nmax], end[Nmax], size[Nmax];
void build(int node, int left, int right, int offset, vector <int> & stripe) {
if(left == right) {
tree[node + offset] = Data[stripe[left - 1]].value;
return;
}
build(leftSon, left, middle, offset, stripe);
build(rightSon, middle + 1, right, offset, stripe);
if(tree[leftSon + offset] > tree[rightSon + offset])
tree[node + offset] = tree[leftSon + offset];
else
tree[node + offset] = tree[rightSon + offset];
}
void update(int node, int left, int right, int offset, int & index, int & value) {
if(left == right) {
tree[node + offset] = value;
return;
}
if(index <= middle)
update(leftSon, left, middle, offset, index, value);
else
update(rightSon, middle + 1, right, offset, index, value);
if(tree[leftSon + offset] > tree[rightSon + offset])
tree[node + offset] = tree[leftSon + offset];
else
tree[node + offset] = tree[rightSon + offset];
}
int query(int node, int left, int right, int offset, int & a, int & b) {
if(a <= left && right <= b)
return tree[node + offset];
int result = 0;
if(a <= middle)
result = query(leftSon, left, middle, offset, a, b);
if(middle < b)
result = max(result, query(rightSon, middle + 1, right, offset, a, b));
return result;
}
public:
segmentTrees() {
top = 0;
end[0] = 0;
}
void create(vector <int> & stripe) {
size[++top] = stripe.size();
start[top] = end[top - 1] + 1;
end[top] = start[top] + (size[top] << 2);
build(root, 1, size[top], start[top] - 1, stripe);
}
void update(int & node, int & newValue) {
Data[node].value = newValue;
update(root, 1, size[Data[node].stripeId], start[Data[node].stripeId] - 1, Data[node].position, newValue);
}
int query(int & nodeA, int & nodeB) {
int a, b, id, result = 0;
while(Data[nodeA].stripeId != Data[nodeB].stripeId) {
if(Data[Data[Stripe[Data[nodeA].stripeId].back()].father].level
> Data[Data[Stripe[Data[nodeB].stripeId].back()].father].level)
swap(nodeA, nodeB);
result = max(result, query(root, 1, size[Data[nodeB].stripeId], start[Data[nodeB].stripeId] - 1, Data[nodeB].position, size[Data[nodeB].stripeId]));
nodeB = Data[Stripe[Data[nodeB].stripeId].back()].father;
}
id = Data[nodeA].stripeId;
a = Data[nodeA].position;
b = Data[nodeB].position;
if(a > b)
swap(a, b);
return max(result, query(root, 1, size[id], start[id] - 1, a, b));
}
} sts;
void Dfs(int node, int father, int level) {
int i, one = 0;
Data[node].father = father;
Data[node].level = level;
Data[node].sons = 1;
for(i = 0; i < Tree[node].size(); i++)
if(son != father) {
Dfs(son, node, level + 1);
Data[node].sons += Data[son].sons;
if(Data[son].sons > Data[one].sons)
one = son;
}
if(Data[node].sons == 1) {
Stripe[++countStripes].push_back(node);
Data[node].stripeId = countStripes;
Data[node].position = 1;
}
else {
Stripe[Data[one].stripeId].push_back(node);
Data[node].stripeId = Data[one].stripeId;
Data[node].position = Stripe[Data[one].stripeId].size();
}
}
void Prepare() {
Dfs(1, 0, 1);
for(int i = 1; i <= countStripes; i++)
sts.create(Stripe[i]);
}
void Read(ifstream & in, int & queries) {
int i, x, y;
in >> N >> queries;
for(i = 1; i <= N; i++)
in >> Data[i].value;
for(i = 1; i < N; i++) {
in >> x >> y;
Tree[x].push_back(y);
Tree[y].push_back(x);
}
}
int main() {
int x, y, type, queries;
ifstream in("heavypath.in");
ofstream out("heavypath.out");
Read(in, queries);
Prepare();
while(queries--) {
in >> type >> x >> y;
if(type == 0)
sts.update(x, y);
else
out << sts.query(x, y) << '\n';
}
in.close();
out.close();
return 0;
}