/// 22:50
#include <fstream>
#include <iostream>
#include <vector>
#define DIM 100010
using namespace std;
ifstream fin ("heavypath.in");
ofstream fout ("heavypath.out");
int Size[DIM],whatChain[DIM],positionInChain[DIM],chainFatherNode[DIM],level[DIM];
int a[40][2*DIM],val[DIM],viz[DIM];
vector <int> chains[DIM],L[DIM];
int n,m,i,x,y,op,nr_chains,sol;
void dfs (int nod, int tata){
viz[nod] = Size[nod] = 1;
level[nod] = 1+level[tata];
for (int i=0;i<L[nod].size();i++){
int vecin = L[nod][i];
if (!viz[vecin]){
dfs (vecin,nod);
Size[nod] += Size[vecin]; /// cate noduri am in subarbore
}}
if (L[nod].size() == 1 && nod != 1){ /// inseamna ca e frunza deci incep un nou lant
nr_chains++;
chains[nr_chains].push_back(0); /// le vreau indexate de la 1
chains[nr_chains].push_back(nod);
whatChain[nod] = nr_chains; /// pe ce lant se afla x
positionInChain[nod] = 1; /// pozitia in lant
} else {
/// pun nodul curent la capatul lantului fiului cel mai "greu", adica cel care Size ul maxim
int maxim = 0, poz = 0;
for (int i=0;i<L[nod].size();i++){
int fiu = L[nod][i];
if (fiu == tata)
continue;
if (Size[fiu] > maxim){
maxim = Size[fiu];
poz = fiu;
}}
/// acum il atasez la fiu
chains[whatChain[poz]].push_back(nod);
whatChain[nod] = whatChain[poz];
positionInChain[nod] = chains[whatChain[poz]].size()-1;
/// acum calculez chain father node
for (int i=0;i<L[nod].size();i++){
int fiu = L[nod][i];
if (fiu == tata)
continue;
if (fiu != poz)
chainFatherNode[whatChain[fiu]] = nod;
}}}
inline void build (int chain, int nod, int st, int dr){
if (st == dr){
a[chain][nod] = val[chains[chain][st]];
return;
}
int mid = (st+dr)/2;
build (chain,2*nod,st,mid);
build (chain,2*nod+1,mid+1,dr);
a[chain][nod] = max (a[chain][2*nod],a[chain][2*nod+1]);
}
inline void update (int chain, int nod, int st, int dr, int poz, int val){
if (st == dr){
a[chain][nod] = val;
return;
}
int mid = (st+dr)/2;
if (poz <= mid)
update (chain,2*nod,st,mid,poz,val);
else update (chain,2*nod+1,mid+1,dr,poz,val);
a[chain][nod] = max (a[chain][2*nod],a[chain][2*nod+1]);
}
inline void query_aint (int chain, int nod, int st, int dr, int x, int y, int &maxi){
if (x <= st && dr <= y){
maxi = max (maxi,a[chain][nod]);
return;
}
int mid = (st+dr)/2;
if (x <= mid)
query_aint (chain,2*nod,st,mid,x,y,maxi);
if (y > mid)
query_aint (chain,2*nod+1,mid+1,dr,x,y,maxi);
}
inline void query (int x, int y){
if (x == y)
return;
if (whatChain[x] == whatChain[y]){ /// aici e simplu
int maxi = 0;
int posx = positionInChain[x];
int posy = positionInChain[y];
if (posx > posy)
swap (posx,posy);
query_aint (whatChain[x],1,1,chains[whatChain[x]].size()-1,posx,posy,maxi);
sol = max (sol,maxi);
return;
}
/// acum trebuie sa vad pe care il urc
if (level[chainFatherNode[whatChain[x]]] > level[chainFatherNode[whatChain[y]]]){
/// il urc pe x
int maxi = 0;
query_aint (whatChain[x],1,1,chains[whatChain[x]].size()-1,positionInChain[x],chains[whatChain[x]].size()-1,maxi);
sol = max (sol,maxi);
int nr = chainFatherNode[whatChain[x]]; /// nodul fix deasupra lantului lui x
query (nr,y);
} else {
int maxi = 0;
query_aint (whatChain[y],1,1,chains[whatChain[y]].size()-1,positionInChain[y],chains[whatChain[y]].size()-1,maxi);
sol = max (sol,maxi);
int nr = chainFatherNode[whatChain[y]];
query (x,nr);
}}
int main (){
fin>>n>>m;
for (i=1;i<=n;i++)
fin>>val[i];
for (i=1;i<n;i++){
fin>>x>>y;
L[x].push_back(y);
L[y].push_back(x);
}
dfs (1,0);
//for (i=1;i<=n;i++)
// cout<<whatChain[i]<<" "<<positionInChain[i]<<"\n";
for (i=1;i<=nr_chains;i++){
//cout<<chainFatherNode[i]<<"\n";
for (int j=1;j<chains[i].size();j++)
cout<<chains[i][j]<<" ";
cout<<"\n";
}
/// acum trebuie sa fac build ul la aint
for (i=1;i<=nr_chains;i++)
build (i,1,1,chains[i].size()-1);
for (i=1;i<=m;i++){
fin>>op>>x>>y;
if (op == 0) /// update
update (whatChain[x],1,1,chains[whatChain[x]].size()-1,positionInChain[x],y);
else {
sol = 0;
query (x,y);
fout<<sol<<"\n";
}}
return 0;
}