#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <bitset>
#include <cmath>
#define Nmax 100005
#define DIM 666013
#define next ((++pointer) == DIM) ? (fread(buffer,1,DIM,stdin),pointer = 0) : 0
char buffer[DIM];
int pointer = DIM - 1;
void scanf( int &A )
{
A = 0;
for(;'0' > buffer[pointer] || buffer[pointer] > '9';next);
for(;'0' <= buffer[pointer] && buffer[pointer] <= '9'; next)
A = A * 10 + buffer[pointer] - 48;
}
using namespace std;
int valori[Nmax];
int care_lantz[Nmax]; /// care_lantz[i] -> in ce lantz se afla i
int tata_lantz[Nmax]; /// tata_lantz[i] -> de ce nod se leaga lantul i
int greutate[Nmax]; /// greutate[i] -> greutatea nodului i
int poz_lantz[Nmax]; /// poz_lantz[i] -> pozitia in lantz a nodului i
int adancime[Nmax]; /// adancime[i] -> adancimea nodului i
int nrl = 1,N,M; /// nr lantzuri
int A,B,_newX,pos,answer,lc;
vector<int> G[Nmax];
vector<int> lantz[Nmax];
bitset<Nmax> used;
class cmp{
public:
bool operator()(const int &x1,const int &x2) const{
return greutate[x1] > greutate[x2];
}
};
class SegmentTree{
private:
vector<int> range;
public:
void dimensiune(int k)
{
int x = ceil(log2(1.0*k));
range.resize(1<<(x+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;
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);
}
}Aint[Nmax];
void read()
{
scanf(N);
scanf(M);
for(int i = 1; i <= N; ++i)
scanf(valori[i]);
}
void DFS_weight(int k)
{
used[k] = 1;
for(vector<int>::iterator it = G[k].begin(); it != G[k].end(); ++it)
if(!used[*it])
{
adancime[*it] = adancime[k] + 1;
DFS_weight(*it);
greutate[k] += greutate[*it];
}
greutate[k] += 1; /// greutatea nodului curent
}
void build_tree()
{
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 decompose(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)
{
++nrl;
lantz[nrl].push_back(0);
tata_lantz[nrl] = k;
}
decompose(*it);
}
}
void path_decomposition()
{
adancime[1] = 1;
DFS_weight(1);
used = 0;
for(int i = 0; i <= N; ++i)
sort(G[i].begin(),G[i].end(),cmp());
lantz[nrl].push_back(0);
decompose(1);
}
void raspunde(int nod1,int nod2)
{
answer = -1;
while(care_lantz[nod1] != care_lantz[nod2])
{
if(adancime[tata_lantz[care_lantz[nod1]]] < adancime[tata_lantz[care_lantz[nod2]]])
swap(nod1,nod2);
if(care_lantz[nod1] == 1)
swap(nod1,nod2);
lc = care_lantz[nod1];
A = 1;
B = poz_lantz[nod1];
Aint[lc].Querry(1,lantz[lc].size()-1,1);
nod1 = tata_lantz[lc];
}
lc = care_lantz[nod1];
A = poz_lantz[nod1];
B = poz_lantz[nod2];
if(A > B) swap(A,B);
Aint[lc].Querry(1,lantz[lc].size()-1,1);
printf("%d\n",answer);
}
void querries()
{
for(int i = 1; i <= nrl; ++i)
{
lc = i;/// lantul curent
Aint[i].dimensiune(lantz[i].size());
Aint[i].Build(1,lantz[i].size()-1,1);
}
int x,y,op;
for(int i = 1; i <= M; ++i)
{
scanf(op);
scanf(x),scanf(y);
if(op == 0)
{
pos = poz_lantz[x];
_newX = y;
lc = care_lantz[x];
Aint[lc].Update(1,lantz[lc].size()-1,1);
}
else
raspunde(x,y);
}
}
int main()
{
freopen("heavypath.in","r",stdin);
freopen("heavypath.out","w",stdout);
read();
build_tree();
path_decomposition();
querries();
return 0;
}