#include<iostream>
#include<deque>
#include<algorithm>
#include<fstream>
#include<vector>
using namespace std;
ifstream in("heavypath.in");
ofstream out("heavypath.out");
const int mx=2e5;
int n,m,num_paths,v[mx],subtree[mx],path_father[mx],node_path[mx],path_depth[mx],node_index[mx];
vector<int> g[mx];
vector<int> path_list[mx];
vector<int> path_tree[mx];
void build_tree(int index, int path_index ,int l,int r){
if(l==r){
path_tree[path_index][index]=v[path_list[path_index][l]];
}
else{
int middle=(l+r)/2;
build_tree(index*2,path_index,l,middle);
build_tree(index*2+1,path_index,middle+1,r);
path_tree[path_index][index]=max(path_tree[path_index][index*2],path_tree[path_index][index*2+1]);
}
}
int query_tree(int index,int path_index,int l,int r,int a,int b){
if(l>=a && r<=b){
return path_tree[path_index][index];
}
else{
int middle=(l+r)/2;
int result=-1;
if(a<=middle){
result=max(result,query_tree(index*2,path_index,l,middle,a,b));
}
if(b>middle){
result=max(result,query_tree(index*2+1,path_index,middle+1,r,a,b));
}
return result;
}
}
void update_tree(int index,int path_index,int l,int r,int pos,int value){
if(l==r){
path_tree[path_index][index]=value;
}
else{
int middle=(l+r)/2;
if(pos<=middle){
update_tree(index*2,path_index,l,middle,pos,value);
}
else{
update_tree(index*2+1,path_index,middle+1,r,pos,value);
}
path_tree[path_index][index]=max(path_tree[path_index][index*2],path_tree[path_index][index*2+1]);
}
}
void build_segment_trees(){
for(int i=1;i<num_paths;i++){
path_tree[i].resize(path_list[i].size()*4);
build_tree(1,i,0,path_list[i].size()-1);
}
}
int query_tree(int path_index,int l,int r){
int result= query_tree(1,path_index,0,path_list[path_index].size()-1,l,r);
return result;
}
void read(){
in>>n>>m;
for(int i=1;i<=n;i++){
in>>v[i];
}
int a,b;
for(int i=0;i<n-1;i++){
in>>a>>b;
g[a].push_back(b);
g[b].push_back(a);
}
}
void dfs(int node,int father){
subtree[node]=1;
for(int k:g[node]){
if(k==father)
continue;
dfs(k,node);
subtree[node]+=subtree[k];
}
}
void heavypath(int node,int father,int& path){
int current_path=path;
path_list[current_path].push_back(node);
node_path[node]=current_path;
node_index[node]=path_list[current_path].size()-1;
int max_sub_size=-1,best_node=0;
for(int k:g[node]){
if(k==father)
continue;
if(subtree[k]>max_sub_size){
max_sub_size=subtree[k];
best_node=k;
}
}
if(max_sub_size!=-1){
heavypath(best_node,node,path);
}
for(int k:g[node]){
if(k==father || k==best_node)
continue;
path++;
path_father[path]=node;
path_depth[path]=path_depth[current_path]+1;
heavypath(k,node,path);
}
}
int query(int a,int b){
int result=0;
while(node_path[a]!=node_path[b]){
int path_a=node_path[a],path_b=node_path[b];
if(path_depth[path_a]>=path_depth[path_b]){
result=max(result,query_tree(path_a,0,node_index[a]));
a=path_father[path_a];
}
else{
result=max(result,query_tree(path_b,0,node_index[b]));
b=path_father[path_b];
}
}
int l=min(node_index[a],node_index[b]);
int r=max(node_index[a],node_index[b]);
result=max(result,query_tree(node_path[b],l,r));
return result;
}
void update(int node,int value){
int np=node_path[node],ni=node_index[node];
update_tree(1,np,0,path_list[np].size()-1,ni,value);
}
void solve(){
dfs(1,0);
int path_index=1;
heavypath(1,0,path_index);
num_paths=path_index+1;
build_segment_trees();
int a,b,c;
for(int i=0;i<m;i++){
in>>a>>b>>c;
if(a==1){
out<<query(b,c)<<'\n';
}
else{
update(b,c);
}
}
}
int main(){
read();
solve();
return 0;
}