// O(n * log n * log n)
#include <cstdio>
#include <algorithm>
#include <vector>
#include <fstream>
using namespace std;
#define MAXN 100010
int n, m, lanturi;
int a[MAXN], niv[MAXN], g[MAXN], nl[MAXN], aint[3 * MAXN];
bool v[MAXN];
int lTata[MAXN], lNiv[MAXN], d[MAXN], pozl[MAXN];
vector<int> G[MAXN], l[MAXN];
ifstream fin ("heavypath.in");
void read_graph() {
int i, x, y;
fin >> n >> m;
for (i = 1; i <= n; i++)
fin >> a[i];
for (i = 1; i < n; i++) {
fin >> x >> y;
G[x].push_back(y);
G[y].push_back(x);
}
}
void df(int nod) {
int hN = -1; // fiul cu greutatea cea mai mare
bool frunza = true;
vector<int>::iterator it;
v[nod] = true; g[nod] = 1; // greutatea nodului
for (it = G[nod].begin(); it != G[nod].end(); it++) { // pentru fiecare fiu al lui nod
if (not v[*it]) { // Vecinul nu este vizitat deja?
frunza = false; // Are cel putin un fiu, deci nu e frunza.
niv[*it] = niv[nod] + 1; // Marim nivelul fiului.
df(*it); // Continuam parcurgerea din fiu.
g[nod] += g[*it]; // Greutatea subarborelui care are ca radacina 'nod' se actualizeaza cu greutatea subarborelui care are radacina in fiul curent.
if(hN == -1) // Inca nu s-a actualizat?
hN = *it;
else
if(g[hN] < g[*it])
hN = *it; // Retinem fiul cu greutatea cea mai mare.
}
}
if (frunza) { // Se formeaza un lant nou.
nl[nod] = ++lanturi; // numarul lantului corespunzator nodului 'nod'. Creste numarul de lanturi.
d[lanturi] = 1; // dimensiunea lantului 'lanturi'
l[lanturi].push_back(nod); // Adaugam nodul lantului.
return;
}
nl[nod] = nl[hN]; // Lantul pentru nodul 'nod' este lantul fiului cel mai greu.
d[nl[nod]]++; // inca un nod in acest lant
l[nl[nod]].push_back(nod); // Adaugam nodul lantului.
for (it = G[nod].begin(); it != G[nod].end(); it++) {
if (*it != hN /*and v[*it] /*niv[*it] >= niv[nod]*/) {
lTata[nl[*it]] = nod; // Tatal lantului pe care se afla fiul curent.
lNiv[nl[*it]] = niv[nod];
}
}
}
void build(int nod, int s, int d, int decalaj, int lant) {
int m;
if (s < d) {
m = (s + d) / 2;
build(nod * 2, s, m, decalaj, lant);
build(nod * 2 + 1, m + 1, d, decalaj, lant);
aint[nod + decalaj] = max(aint[nod * 2 + decalaj], aint[nod * 2 + 1 + decalaj]);
}
else
aint[nod + decalaj] = a[l[lant][s - 1]]; // Lantul este indexat de la 0.
}
void make_paths() {
int i;
niv[1] = 1;
df(1);
for(i = 1; i <= lanturi; i++) {
reverse(l[i].begin(), l[i].end()); // Orientam lantul de sus in jos.
if (i > 1)
pozl[i] = pozl[i - 1] + d[i - 1] * 4; // pozitia arborelui de intervale pentru lantul i
build(1, 1, d[i], pozl[i], i);
}
}
void update(int nod, int left, int right, int poz, int val, int decalaj) {
if(left == right) {
aint[nod + decalaj] = val;
return;
}
int med = (left + right) / 2;
if(poz <= med)
update(nod * 2, left, med, poz, val, decalaj);
else
update(nod * 2 + 1, med + 1, right, poz, val, decalaj);
aint[nod + decalaj] = max(aint[nod * 2 + decalaj], aint[nod * 2 + 1 + decalaj]);
}
int query(int nod, int left, int right, int qleft, int qright, int decalaj) {
if(qleft <= left && right <= qright)
return aint[nod + decalaj];
int med = (left + right) / 2, rez = 0;
if(qleft <= med)
rez = max(rez, query(nod * 2, left, med, qleft, qright, decalaj));
if(med < qright)
rez = max(rez, query(nod * 2 + 1, med + 1, right, qleft, qright, decalaj));
return rez;
}
void solve() {
int t, x, y, sol = 0;
for (int i = 1; i <= m; ++i) {
fin >> t >> x >> y;
if (t == 0)
update(1, 1, d[nl[x]], niv[x] - lNiv[nl[x]], y, pozl[nl[x]]);
else {
sol = 0;
while (nl[x] != nl[y]) {
if (lNiv[nl[x]] < lNiv[nl[y]])
swap(x, y);
sol = max(sol, query(1, 1, d[nl[x]], 1, niv[x] - lNiv[nl[x]], pozl[nl[x]]));
x = lTata[nl[x]];
}
if (niv[x] < niv[y])
swap(x, y);
sol = max(sol, query(1, 1, d[nl[x]], niv[x] - lNiv[nl[x]], niv[y] - lNiv[nl[x]], pozl[nl[x]]));
printf("%d\n", sol);
}
}
}
int main() {
freopen("heavypath.out", "w", stdout);
read_graph();
make_paths();
solve();
return 0;
}