#include <iostream>
#include <fstream>
#include <vector>
const int MAXN = 1e5 + 5;
using namespace std;
ifstream in("heavypath.in");
ofstream out("heavypath.out");
/// nod_lant[i] = in ce lant se afla nodul i
/// pozitie[i] = pe ce pozitie se afla in lantul (deja stiut) nodul i
/// inaltime[i] = care este inaltimea lantului care se afla pe pozitia i
/// daca am lantul pe ex : 4 3 2 1(e luat de la spate in fata din cauza la dfs)
/// atunci nod_lant[4] = nod_lant[3] = nod_lant[2] = nod_lant[1] = 1 primul lant
/// pozitie[4] = 1 in lant se alfa pe poz 1, pozitie[3] = 2, pozitie[2] = 3, pozitie[1] = 4
/// inaltime[1] = 1, se afla la inaltimea 1, dar lantul 9 8 7 se afla la inaltimea 2
/// daca citeste cineva asta sper sa inteleaga :)
/// singurul vector indexat de la 0 este lant[i]
int tata[MAXN],dimensiune[MAXN],nod_lant[MAXN],pozitie[MAXN],inaltime[MAXN];
int valori[MAXN];
bool viz[MAXN];
int nr_lanturi;
vector<int>arbore_intervale[MAXN];/// Sunt indexati de la 1!!!!!
vector<int>lant[MAXN];/// indexati de la 0!!
vector<int>graf[MAXN];
void dfs(int nod,int anterior = -1){
dimensiune[nod] = 1;
viz[nod] = true;
int curent;
for(int i = 0; i < graf[nod].size(); i++){
curent = graf[nod][i];
if(curent == anterior){
/// anteriorul este nodul care a fost parcurs inainte
/// daca eu vreau sa ma duc cu o muchie in spate atunci tatal nodului curent este anterior
tata[nod] = anterior;
}
if(!viz[curent]){
dfs(curent,nod);
dimensiune[nod] += dimensiune[curent];
}
}
if(graf[nod].size() == 1 && nod != 1){
lant[++nr_lanturi].push_back(nod);
nod_lant[nod] = nr_lanturi;
pozitie[nod] = 1; /// il pun pe pozitia 1
}else{
int vecin_bun,maxim = 0;
for(int i = 0; i < graf[nod].size(); i++){
int curent = graf[nod][i];
if(dimensiune[curent] > maxim && curent != anterior){
vecin_bun = curent;
maxim = dimensiune[curent];
}
}
int unde = nod_lant[vecin_bun];
lant[unde].push_back(nod); /// adaug nodul nod la lantul cu subarborele maxim
nod_lant[nod] = unde;
pozitie[nod] = lant[unde].size();/// il pun pe ultima pozitie
for(int i = 0; i < graf[nod].size(); i++){
int curent = graf[nod][i];
if(nod_lant[curent] != nod_lant[nod])
inaltime[nod_lant[curent]] = inaltime[nod_lant[nod]] + 1; /// modific inaltimea lantului
}
}
}
void update(int index_lant,int val,int pozitie,int st,int dr,int nod = 1){
///cout<<st<<" "<<dr<<" "<<pozitie<<" "<<nod<<endl;
if(st == dr && st == pozitie){
arbore_intervale[index_lant][nod] = val;
return;
}
int mij = (st + dr) / 2;
if(pozitie <= mij)
update(index_lant,val,pozitie,st,mij,nod * 2);
else
update(index_lant,val,pozitie,mij + 1,dr,nod * 2 + 1);
if(nod * 2 + 1 < arbore_intervale[index_lant].size()) /// sa nu cumva sa ies din size
arbore_intervale[index_lant][nod] = max(arbore_intervale[index_lant][nod * 2],arbore_intervale[index_lant][nod * 2 + 1]);
}
int query(int index_lant,int query_left,int query_right,int st,int dr,int nod = 1){
///cout<<st<<" "<<dr<<" "<<nod<<" "<<arbore_intervale[index_lant][nod]<<endl;
if(query_left <= st && dr <= query_right)
return arbore_intervale[index_lant][nod];
int mij = (st + dr) / 2;
int val_stanga = 0,val_dreapta = 0;
if(query_left <= mij)
val_stanga = query(index_lant,query_left,query_right,st,mij,nod * 2);
if(mij + 1 <= query_right)
val_dreapta = query(index_lant,query_left,query_right,mij + 1,dr,nod * 2 + 1);
return max(val_stanga,val_dreapta);
}
int query_lant(int nod){
int index_lant = nod_lant[nod];
int dimensiune = lant[index_lant].size();
int poz = pozitie[nod];/// pozitie e de la 1 indexat
return query(index_lant,poz,dimensiune,1,dimensiune);
}
int intrebare(int a,int b){
/// calculez usor usor lca intre a si b
if(inaltime[nod_lant[a]] < inaltime[nod_lant[b]])
swap(a,b);
/// a este intr-un lant mai sus decat b
int maxim = max(query_lant(a),query_lant(b));
while(inaltime[nod_lant[a]] > inaltime[nod_lant[b]]){
int urmatorul = lant[nod_lant[a]].back();/// ultimul element
a = tata[urmatorul];
maxim = max(maxim,query_lant(a));
}
/// sunt pe aceasi inaltime
while(nod_lant[a] != nod_lant[b]){
int urmatorul = lant[nod_lant[a]].back();
a = tata[urmatorul];
maxim = max(maxim,query_lant(a));
urmatorul = lant[nod_lant[b]].back();
b = tata[urmatorul];
maxim = max(maxim,query_lant(b));
}
/// sunt in acelasi lant
/// eu vreau ca a sa fie mai jos pe lant
/// vectorul de pozitii este pe invers adica pt lantul 4 3 2 1, poz : 1 2 3 4
/// vreau ca poz[a] < poz[b] daca nu, fac este swap(a,b)
///cout<<a<<" "<<b<<" "<<" pozitii : "<<pozitie[a]<<" "<<pozitie[b]<<endl;
if(pozitie[a] > pozitie[b])
swap(a,b);
int query_pe_lant = query(nod_lant[a],pozitie[a],pozitie[b],1,lant[nod_lant[a]].size());
maxim = max(maxim,query_pe_lant);
/// acum stiu clar ca a = b;
out<<maxim<<"\n";
}
void construieste_arbore(int index_lant){
vector<int>arbore(4 * lant[index_lant].size() + 2);
arbore_intervale[index_lant] = arbore;
int lung_lant = lant[index_lant].size();
for(int i = 0; i < lant[index_lant].size(); i++){
int val = valori[lant[index_lant][i]];
update(index_lant,val,i + 1,1,lung_lant);/// !! indexati de la 1 de aia poz = i + 1
}
}
int main()
{
int n,intrebari;
in>>n>>intrebari;
for(int i = 1; i <= n; i++)
in>>valori[i];
for(int i = 1; i <= n - 1; i++){
int x,y;
in>>x>>y;
graf[x].push_back(y);
graf[y].push_back(x);
}
inaltime[1] = 1;
dfs(1);
for(int i = 1; i <= n; i++)
construieste_arbore(i);
for(int i = 1; i <= intrebari; i++){
int cerinta,a,b;
in>>cerinta>>a>>b;
if(cerinta == 1){
intrebare(a,b);
}else{
int index_lant = nod_lant[a];
int pozitie_lant = pozitie[a];
update(index_lant,b,pozitie_lant,1,lant[index_lant].size());
}
}
return 0;
}