#include<bits/stdc++.h>
using namespace std;
ifstream f("heavypath.in");
ofstream g("heavypath.out");
const int N = 1<<17;
vector<int> v[N],P[N],p;
int n,q,i,x,y,t,z,Lg[N],V[N],niv[N],w[N],a[N],T[N],na,ST,DR,poz[N],DAD[N],Querry(int,int),QuerryA(int,int,int,int,int,int);
void df(int),AINT(int);
int main()
{
f>>n>>q;
for(i=1; i<=n; i++)
f>>V[i];
for(i=1; i<n; i++)
{
f>>x>>y;
v[x].push_back(y);
v[y].push_back(x);
}
niv[1]=1;
df(1);
//am drumurile si constriuesc acum arborii de intervale
for(i=1; i<=na; i++)
{
p.resize(0);
DR=0;
for(; P[i].size();)
{
x=P[i].back();
P[i].pop_back();
p.push_back(x);
DR++;
poz[x]=DR;
}
AINT(i);//construiesc arborele de intrvale corespunzator
}
for(; q; q--)
{
f>>t>>x>>y;
if(!t)
{
i=a[x];
int j=poz[x]+Lg[i]-1;
P[i][j]=y;
for(j/=2; j; j/=2)P[i][j]=max(P[i][2*j],P[i][2*j+1]);
continue;
}
g<<Querry(x,y)<<'\n';
}
return 0;
}
void df(int nod)
{
//setez greutatea nodului la 1;
w[nod]=1;
//daca nodul este frunza
if(v[nod].size()==1&&nod!=1)
{
//construiesc un nou drum(care de fapt va fi un nou arbore de intervale)
a[nod]=++na;
P[na].push_back(nod);
niv[nod]=niv[T[nod]]+1;
DAD[a[nod]]=T[nod];
return;
}
//daca nodul nu este frunza
for(auto vec: v[nod])
{
//folosind tatal nodului calculez nivelul nodului
if(vec==T[nod])
{
niv[nod]=niv[vec]+1;
continue;
}
//pentru fii apelez df
T[vec]=nod;
df(vec);
//folosesc greutatea fiilor pentru a calcula greutatea nodului
w[nod]+=w[vec];
}
//incep a doua parcurgere sa determin fiul de greutate maxima
int bst=0;
for(auto vec: v[nod])
//nu ma interezeaza tatal si fii de greutati mai mici decat cea mai buna de pana acum
if(vec!=T[nod]&&w[vec]>bst)
{
//daca gasesc greutate mai mare updatez si redefinesc drumul la care lipesc nodul curent
bst=w[vec];
a[nod]=a[vec];
}
//pun nodul curent in arborele de intervale potrivit si actulaizez nodul la care se va lipi aceste
P[a[nod]].push_back(nod);
DAD[a[nod]]=T[nod];
}
int Querry(int x1,int x2)
{
if(a[x1]==a[x2]) return QuerryA(a[x1],1,min(poz[x1],poz[x2]),max(poz[x1],poz[x2]),1,Lg[a[x1]]);
if(niv[DAD[a[x1]]]<niv[DAD[a[x2]]])
swap(x1,x2);
return max(QuerryA(a[x1],1,1,poz[x1],1,Lg[a[x1]]),Querry(DAD[a[x1]],x2));
}
int QuerryA(int ar,int nod,int lo,int hi,int LO,int HI)
{
if(lo>HI||hi<LO)return 0;
if(lo<=LO&&HI<=hi)return P[ar][nod];
int MI=(LO+HI)/2;
return max(QuerryA(ar,2*nod,lo,hi,LO,MI),QuerryA(ar,2*nod+1,lo,hi,MI+1,HI));
}
void AINT(int i)
{
int m=p.size();
Lg[i]=1;
while(Lg[i]<m)
Lg[i]<<=1;
P[i].resize(2*Lg[i]);
fill(P[i].begin(),P[i].end(),0);
int j=1;
for(auto it:p)
{
P[i][j+Lg[i]-1]=V[it];
j++;
}
for(j=Lg[i]-1;j>=1;j--)
P[i][j]=max(P[i][2*j],P[i][2*j+1]);
}