/***********************
z zzzz
z
******* z
** 66 * 88
** 66 *88888888
** xxx 88888888
*****TRACTORAS****
* x * * x *
* * * *
*********************/
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cmath>
#include <bitset>
#define Nmax 100005
#define DIM 1666013
#define INF 0x3f3f3f3f
using namespace std;
bitset<Nmax> used;
vector<int> lantz[Nmax],G[Nmax];
int valori[Nmax]; /// valori[i] -> valoarea nodului i
int care_lantz[Nmax]; /// care_lantz[i] -> in ce lantz se afla nodul i
int poz_lantz[Nmax]; /// poz_lantz[i] -> pe ce pozitie se afla in lant nodul i
int tata_lantz[Nmax]; /// tata_lantz[i] -> de ce nod se leaga lantul i
int greutate[Nmax]; /// greutate[i] -> greutatea subarborelui i
int adancime[Nmax]; /// adancime[i] -> adancimea nodului i
int _newX,A,B,pos,answer,N,M,LC;
int nrl = 1; /// nr lanturi
char buffer[DIM];
int poz = DIM - 1;
void Scanf(int &A){
A = 0;
while('0' > buffer[poz] || buffer[poz] > '9')
if(++poz == DIM)
fread(buffer,1,DIM,stdin),poz = 0;
while('0' <= buffer[poz] && buffer[poz] <= '9')
{
A = A * 10 + buffer[poz] - 48;
if(++poz == DIM)
fread(buffer,1,DIM,stdin),poz = 0;
}
}
class cmp{
public:
bool operator()(const int &v1,const int &v2)const{
return greutate[v1] > greutate[v2];
}
};
class SegmentTree{
public:
vector<int> range;
void Resize(int lg){
range.resize( 1 <<((int)ceil(log2((double)lg)) + 1));
}
void Build(int li,int lf,int pz)
{
if(li == lf){
range[pz] = valori[lantz[LC][li]];
return;
}
int m = li + ((lf - li) >> 1);
Build(li,m,pz<<1);
Build(m+1,lf,(pz<<1)|1);
range[pz] = max(range[pz<<1],range[(pz<<1)|1]);
}
void Update(int li,int lf,int pz)
{
if(li == lf){
range[pz] = _newX;
valori[li] = _newX;
return;
}
int m = li + ((lf - li) >> 1);
if(pos <= m)
Update(li,m,pz<<1);
else
Update(m+1,lf,(pz<<1)|1);
range[pz] = max(range[pz<<1],range[(pz<<1)|1]);
}
void Querry(int li,int lf,int pz)
{
if(A <= li && lf <= B){
answer = max(answer,range[pz]);
return;
}
int m = li + ((lf - li) >> 1);
if(A <= m)
Querry(li,m,pz<<1);
if(B > m)
Querry(m+1,lf,(pz<<1)|1);
}
};
SegmentTree Aint[Nmax];
void Read()
{
Scanf(N);Scanf(M);
for(int i = 1; i <= N; ++i)
Scanf(valori[i]);
int a,b;
for(int i = 1; i < N; ++i)
{
Scanf(a);Scanf(b);
G[a].push_back(b);
G[b].push_back(a);
}
}
void get_weight(int k)
{
used[k] = 1;
greutate[k] = 1;
for(vector<int>::iterator it = G[k].begin(); it != G[k].end(); ++it)
if(!used[*it]){
adancime[*it] = adancime[k] + 1;
get_weight(*it);
greutate[k] += greutate[*it];
}
}
void descomp(int k)
{
used[k] = 1;
lantz[nrl].push_back(k);
care_lantz[k] = nrl;
poz_lantz[k] = lantz[nrl].size() - 1;
for(vector<int>::iterator it = G[k].begin(); it != G[k].end(); ++it)
if(!used[*it])
{
if(lantz[nrl].back() != k){
lantz[++nrl].push_back(0);
tata_lantz[nrl] = k;
}
descomp(*it);
}
}
void Decompose()
{
adancime[1] = 1;
get_weight(1);
used = 0;
for(int i = 1; i <= N; ++i)
sort(G[i].begin(),G[i].end(),cmp());
lantz[nrl].push_back(0);
descomp(1);
}
void Build_trees()
{
for(LC = 1; LC <= nrl; ++LC) /// fixam lantul curent
{
Aint[LC].Resize(lantz[LC].size() - 1);
Aint[LC].Build(1,lantz[LC].size()-1,1);
}
}
int Search(int x,int y)
{
answer = -INF;
while(care_lantz[x] != care_lantz[y])
{
if(adancime[tata_lantz[care_lantz[x]]] < adancime[tata_lantz[care_lantz[y]]])
swap(x,y); /// deci nodul x trebuie sa fie cel care atunci cand se duce in sus ramane mai jos
if(care_lantz[x] == 1)
swap(x,y); /// nu mai am unde sa il urc pe nodul x, deci obligatoriu urc pe y
LC = care_lantz[x];
A = 1;
B = poz_lantz[x];
Aint[LC].Querry(1,lantz[LC].size()-1,1);
x = tata_lantz[LC];
}
A = poz_lantz[x];
B = poz_lantz[y];
LC = care_lantz[x];
if(A > B) swap(A,B);
Aint[LC].Querry(1,lantz[LC].size()-1,1);
return answer;
}
void Solve()
{
int x,y,op;
for(int i = 1; i <= M; ++i)
{
Scanf(op);Scanf(x);Scanf(y);
if(op == 0)
{/// facem update
LC = care_lantz[x];
_newX = y;
pos = poz_lantz[x];
Aint[LC].Update(1,lantz[LC].size()-1,1);
}
else
printf("%d\n",Search(x,y));
}
}
int main()
{
freopen("heavypath.in","r",stdin);
freopen("heavypath.out","w",stdout);
Read();
Decompose();
Build_trees();
Solve();
return 0;
}