#include<bits/stdc++.h>
using namespace std;
#define INIT ios_base :: sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
#define mp make_pair
#define pb push_back
#define ft first
#define sc second
#define ll long long
#define pii pair<int, int>
#define count_bits __builtin_popcount
inline char get_ (){
const int oo=1000005;
static char buf[oo], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, oo, stdin), p1 == p2) ? EOF : *p1 ++;
}
int read_ () {
char c = get_();
int sum = 0;
while(!(c >= '0' && c <= '9')) c = get_();
while(c >= '0' && c <= '9') sum = sum * 10 + (c - '0'), c = get_();
return sum;
}
int n, m, v[100010];
vector<int> g[100010];
int pos[100010];
int hd[100010];
int par[100010];
int val[100010];
int sz[100010];
bool vr[100010];
//graph traversals and perequisites
void dfs_sz(int s){
vr[s]=true;
int res=1;
for(auto u:g[s]){
if(!vr[u]){
par[u]=s;
dfs_sz(u);
res+=sz[u];
}
}
vr[s]=false;
sz[s]=res;
}
int tmp=0;
int tmp1=0, ent[100010], ex[100010];
void build_path(int s, int h){
tmp++;
tmp1++;
ent[s]=tmp1;
pos[s]=tmp;
val[pos[s] ]=v[s];
hd[s]=h;
pii nxt={0, 0};
for(auto u:g[s]){
if(u!=par[s]){nxt=max(nxt, {sz[u], u});}
}
if(nxt!=mp(0, 0) ){
build_path(nxt.sc, h);
}
for(auto u:g[s]){
if( (u!=par[s]) && (u!=nxt.sc) ){
build_path(u, u);
}
}
tmp1++;
ex[s]=tmp1;
return;
}
//segtree
int tree[400010];
void build_seg(int v, int l, int r){
if(l==r){
tree[v]=val[l];
return;
}
int m=(l+r)>>1ll;
build_seg(v*2, l, m); build_seg(2*v+1, m+1, r);
tree[v]=max(tree[v*2], tree[v*2+1]);
}
int query(int v, int tl, int tr, int l, int r){
if(l>r){
return 0;
}
if( (tl==l) && (tr==r) ){
return tree[v];
}
int m=(tl+tr)>>1ll;
return max( query(v*2, tl, m, l, min(r, m)), query(v*2+1, m+1, tr, max(l, m+1), r ) );
}
void update(int v, int l, int r, int x, int y){
if(l==r){
tree[v]=val[x]=y;
return;
}
int m=(l+r)>>1ll;
if(x<=m){
update(v*2, l, m, x, y);
}
else{
update(2*v+1, m+1, r, x, y);
}
tree[v]=max(tree[v*2], tree[v*2+1] );
}
bool is_anc(int u, int v){
return (ent[u]<ent[v]) && (ex[u]>ex[v]);
}
//get_max answers for a chain from the vertex to an ancestor
//v is anc
int get_max(int u, int v){
int res=0;
while(hd[u]!=hd[v]){
if( !is_anc(hd[u], v) ){
res=max(res, query(1, 1, n, pos[hd[u] ], pos[u]) );
u=par[hd[u]];
}
else{
res=max(res, query(1, 1, n, pos[hd[v] ], pos[v] ) );
v=par[hd[v] ];
}
}
res=max(res, query(1, 1, n,min(pos[u], pos[v]), max(pos[u], pos[v]) ) );
return res;
}
/*
ifstream fin("heavypath.in"); ofstream fout("heavypath.out");
#define cin fin
#define cout fout
*/
int32_t main(){
INIT
freopen("heavypath.in", "r", stdin);
freopen("heavypath.out", "w", stdout);
cin>>n>>m;
for(int i=1; i<=n; i++){
cin>>v[i];
}
for(int i=1; i<n; i++){
int a,b;
cin>>a>>b;
g[a].pb(b);
g[b].pb(a);
}
dfs_sz(1);
build_path(1, 1);
build_seg(1, 1, n);
for(int i=1; i<=m; i++){
int t, x, y;
cin>>t>>x>>y;
if(t==0){
update(1, 1, n, pos[x], y);
}
else{
cout<<get_max(x, y)<<"\n";
}
}
return 0;
}