#include<fstream>
#include<vector>
#define NMAX 100005
#define VEC G[nod][i]
#define son (nod<<1)
#define middle ((left+right)>>1)
using namespace std;
ifstream fin("heavypath.in");
ofstream fout("heavypath.out");
int n,Q,v[NMAX],TT[NMAX],level[NMAX],k,which[NMAX],what[NMAX],TT_log[NMAX],R[NMAX];
int AINT[4*NMAX],Maxx,start[NMAX],K,first[NMAX],last[NMAX];
vector<int> G[NMAX],decomp[NMAX],GN[NMAX];
bool use[NMAX];
void read()
{
fin>>n>>Q;
for(int i=1;i<=n;i++)
fin>>v[i];
for(int i=1,x,y;i<n;i++)
{
fin>>x>>y;
G[x].push_back(y);
G[y].push_back(x);
}
}
void DFS_split(int nod,int tt,int lant)
{
TT[nod]=tt;
level[nod]=level[tt]+1;
first[nod]=++K;
if(G[nod].size()==2 && nod!=1)
{
if(!lant)
k++;
lant=1;
R[k]=max(R[k],nod);
which[nod]=k;
decomp[k].push_back(nod);
}
else
{
if(decomp[k].size()==1)
{
which[decomp[k][0]]=0;
decomp[k].clear();
k--;
}
lant=0;
}
for(size_t i=0;i<G[nod].size();i++)
if(VEC!=tt)
DFS_split(VEC,nod,lant);
last[nod]=K;
}
void build_tree(int nod)
{
int NN;
if(!which[nod])
NN=nod;
else
NN=R[which[nod]];
for(size_t i=0;i<G[nod].size();i++)
if(VEC!=TT[nod])
{
if(!which[VEC] || decomp[which[VEC]].size()==1)
GN[NN].push_back(VEC);
else
if(!use[which[VEC]])
{
GN[NN].push_back(R[which[VEC]]);
use[which[VEC]]=1;
}
build_tree(VEC);
}
}
void build_father(vector<int> *G,int nod,int tt)
{
TT_log[nod]=tt;
level[nod]=level[tt]+1;
for(size_t i=0;i<G[nod].size();i++)
if(VEC!=tt)
build_father(G,VEC,nod);
G[nod].clear();
::G[nod].clear();
}
inline void update(int left,int right,int nod,int val,int poz)
{
if(left==right)
{
AINT[nod]=val;
return;
}
if(poz<=middle)
update(left,middle,son,val,poz);
else
update(middle+1,right,son+1,val,poz);
AINT[nod]=max(AINT[son],AINT[son+1]);
}
void query(int left,int right,int nod,int L,int R)
{
if(left>R || right<L)
return;
if(L<=left && right<=R)
{
Maxx=max(Maxx,AINT[nod]);
return;
}
query(left,middle,son,L,R);
query(middle+1,right,son+1,L,R);
}
void Max(int &A,int &maxim)
{
Maxx=0;
if(which[A])
{
query(1,n,1,start[which[A]],what[A]);
maxim=max(maxim,Maxx);
A=TT_log[R[which[A]]];
}
else
{
maxim=max(maxim,v[A]);
A=TT[A];
}
}
int LCA_split(int x,int y)
{
int maxim=0;
while(level[x]>level[y])
Max(x,maxim);
while(level[x]<level[y])
Max(y,maxim);
while(x!=y)
{
Max(x,maxim);
Max(y,maxim);
}
maxim=max(maxim,v[x]);
return maxim;
}
int main()
{
read();
DFS_split(1,0,0);
for(int i=1,L=1;i<=k;i++)
{
start[i]=L;
for(size_t j=0;j<decomp[i].size();j++,L++)
{
what[decomp[i][j]]=L;
update(1,n,1,v[decomp[i][j]],L);
}
decomp[i].clear();
}
build_tree(1);
build_father(GN,1,0);
for(int op,x,y;Q;Q--)
{
fin>>op>>x>>y;
if(!op)
{
if(!which[x])
v[x]=y;
else
update(1,n,1,y,what[x]);
continue;
}
Maxx=0;
//acelasi nod
if(x==y)
{
fout<<v[x]<<'\n';
continue;
}
//acelasi grup
if(which[x]==which[y] && which[x])
{
x=what[x];
y=what[y];
if(x>y)
swap(x,y);
query(1,n,1,x,y);
fout<<Maxx<<'\n';
continue;
}
if(first[x]>first[y])
swap(x,y);
//x e stramos a lui y
if(first[y]<=last[x])
{
int maxim=0;
if(which[x])
{
query(1,n,1,what[x],start[which[x]+1]-1);
maxim=max(maxim,Maxx);
x=R[which[x]];
}
else
maxim=max(maxim,v[x]);
while(TT_log[y]!=x)
Max(y,maxim);
Max(y,maxim);
fout<<maxim<<'\n';
continue;
}
//x si y se afla in subarbori diferiti
else
fout<<LCA_split(x,y)<<'\n';
}
return 0;
}