#include <fstream>
#include <iostream>
#include <vector>
#include <bitset>
#define DIM 100010
#define INF 2000000010
using namespace std;
ifstream fin("heavypath.in");
ofstream fout("heavypath.out");
vector<int> L[DIM]; //lista de vecini
vector<int> Lant[DIM]; //Lant[i] = nodurile din lantul i
int nrLant[DIM]; //nrLant[i] = numarul lantului in care e nodul i
int N[DIM]; //N[i] = nivelul nodului i
int T[DIM]; //T[i] = tatal lantului i (nod care nu e in lantul i)
int Tata[DIM]; //Tata[i] = tatal nodului i in arbore
int P[DIM]; //P[i] = pozitia nodului i in lantul sau
int Start[DIM]; //Start[i] = pozitia la care incepe lantul i in arborele de intervale
int Desc[DIM]; //Desc[i] = numarul de noduri din subarborele lui i
bitset<DIM> U;
int A[4*DIM];
int V[DIM];
int nrLanturi; //numarul total de lanturi
int n, m, i, j, l1, l2, q, x, y, k, t, a, b, maxim;
void dfs(int nod, int niv, int tata) {
N[nod] = niv;
U[nod] = 1;
Desc[nod] = 1;
Tata[nod] = tata;
int maximDesc = 0;
int fiuMaximDesc;
for (int i=0;i<L[nod].size();i++) {
int fiu = L[nod][i];
if (!U[fiu]) {
dfs(fiu, niv+1, nod);
Desc[nod] += Desc[fiu];
//Tata[fiu] = nod;
if (Desc[fiu] > maximDesc) {
maximDesc = Desc[fiu];
fiuMaximDesc = fiu;
}
}
}
if (maximDesc == 0) {
Lant[++nrLanturi].push_back(nod);
nrLant[nod] = nrLanturi;
T[nrLanturi] = Tata[nod];
} else {
Lant[nrLant[fiuMaximDesc]].push_back(nod);
nrLant[nod] = nrLant[fiuMaximDesc];
T[nrLant[fiuMaximDesc]] = Tata[nod];
}
}
void swap(int &a, int &b) {
int aux = a;
a = b;
b = aux;
}
void update(int nod, int st, int dr, int p, int x, int decalaj) {
//A[nod + decalaj] este maximul informatiilor nodurilor aflate intre
//pozitiile st si dr in lantul Lant[lant] si care in arborele de
if (st == dr) {
A[nod+decalaj] = x;
return;
}
int mid = (st+dr)/2;
if (p<=mid)
update(2*nod, st, mid, p, x, decalaj);
if (p>mid)
update(2*nod+1, mid+1, dr, p, x, decalaj);
A[nod+decalaj] = max(A[2*nod+decalaj], A[2*nod+1+decalaj]);
}
int query(int nod, int st, int dr, int a, int b, int decalaj) {
if (a<=st && dr<=b)
return A[nod+decalaj];
int mid = (st + dr)/2;
int qst = 0, qdr=0;
if (a <= mid)
qst = query(2*nod, st, mid, a, b, decalaj);
if (b > mid)
qdr = query(2*nod+1, mid+1, dr, a, b, decalaj);
return max(qst, qdr);
}
int main() {
fin>>n>>q;
for (i=1;i<=n;i++)
fin>>V[i];
for (i=1;i<n;i++) {
fin>>x>>y;
L[x].push_back(y);
L[y].push_back(x);
}
dfs(1,1,0);
// sortez crescator dupa nivel nodurile din acelasi lant
for (i=1;i<=nrLanturi;i++) {
Start[i] = Start[i-1] + 4*Lant[i-1].size();
for (j=0,k=Lant[i].size()-1;j<k;j++,k--) {
swap(Lant[i][j], Lant[i][k]);
P[Lant[i][j]] = j;
P[Lant[i][k]] = k;
}
P[Lant[i][j]] = j;
}
for (i=1;i<=nrLanturi;i++) {
for (j=0;j<Lant[i].size();j++) {
cout<<Lant[i][j]<<" ";
}
cout<<"\n";
}
for (i=1;i<=n;i++) {
update(1, 0, Lant[ nrLant[ i ] ].size()-1, P[i], V[i], Start[ nrLant[ i ] ] );
}
for (;q--;) {
fin>>t>>a>>b;
if (t == 0) {
//V[a] = b;
update(1, 0, Lant[ nrLant[ a ] ].size()-1, P[a], b, Start[ nrLant[ a ] ] );
} else {
//maximul de pe drumul dintre nodurile a si b
maxim = 0;
do {
l1 = nrLant[a];
l2 = nrLant[b];
if (l1 == l2) {
maxim = max(maxim, query(1, 0, Lant[ l1 ].size()-1, min( P[a], P[b] ), max(P[a], P[b]), Start[l1] ) );
break;
}
if ( N[T[l1]] > N[ T[l2] ] ) {
maxim = max(maxim, query(1, 0, Lant[ l1 ].size()-1, 0, P[a], Start[l1] ) );
a = T[l1];
} else {
maxim = max(maxim, query(1, 0, Lant[ l2 ].size()-1, 0, P[b], Start[l2] ) );
b = T[l2];
}
} while (1);
fout<<maxim<<"\n";
}
}
return 0;
}