#include <bits/stdc++.h>
using namespace std;
const int MAX = 1e5 + 14;
vector <int> graf[MAX];
vector <int> path[MAX];
int level[MAX], tata[MAX], sub[MAX], chain[MAX], values[MAX], poz[MAX];
int numarLanturi = 0;
vector <int> tree [MAX];
void dfs(int nod){
sub[nod] = 1;
for (auto x : graf[nod]){
if (x != tata[nod]){
tata[x]= nod;
level[x] = level[nod] + 1;
dfs(x);
sub[nod] += sub[x];
}
}
if(sub [nod] == 1){
// leaf
++ numarLanturi;
chain[nod] = numarLanturi;
path[chain[nod]].push_back(nod);
poz[nod] = path[chain[nod]].size() - 1;
}
else{
int bestSon = 0;
for (auto &son : graf [nod])
{
if (son != tata [nod])
{
if (sub [son] > sub [bestSon])
bestSon = son;
}
}
chain[nod] = chain[bestSon];
path[chain[nod]].push_back(nod);
poz[nod] = path[chain[nod]].size() - 1;
}
}
void update (vector <int> &tree, int nod, int stanga, int dreapta, const int pozitie, const int valoare){
if (stanga == dreapta) { // in frunza
tree[nod] = valoare;
return;
}
int mij = (stanga + dreapta) >> 1;
if (pozitie <= mij)
update(tree, nod * 2, stanga, mij, pozitie, valoare);
else
update(tree, nod * 2 + 1, mij + 1, dreapta, pozitie, valoare);
tree[nod] = max(tree[2 * nod], tree[2 * nod + 1]);
}
int getMaximum(vector<int> &tree, int nod, int stanga, int dreapta, const int a, const int b){
if (a <= stanga and dreapta <= b){
return tree[nod];
}
int mijloc = (stanga + dreapta) / 2;
int maxim = 0;
if (a <= mijloc){
maxim = max(getMaximum(tree, nod * 2, stanga, mijloc, a, b), maxim);
}
if (mijloc < b){
maxim = max(getMaximum(tree, nod * 2 + 1, mijloc + 1, dreapta, a, b), maxim);
}
return maxim;
}
int query(int x, int y){/*
int retinut = values[x];
while (chain[x] != chain[y]){
if(chain[x] == chain[y]){
retinut = max(retinut, maximDinArboreDeintervale(path[chain[y]][poz[y]],chain[x], chain[y]));
y = x;
}
else{
retinut = max(retinut, getMaximum(path[chain[y]][poz[y]], 1, chain[y]));
y = path[chain[y]][poz[y]];
}
}*/
int result = 0;
while (chain [x] != chain[y])
{
if (level[tata[path[chain[x]].back()]] > level [tata[path[chain[y]].back()]])
{
result = max (result, getMaximum(tree[chain[x]], 1, 0, path[chain[x]].size() - 1, poz[x], path[chain[x]].size() - 1));
x = tata[path[chain[x]].back()];
}
else
{
result = max (result, getMaximum(tree[chain[y]], 1, 0, path[chain[y]].size() - 1, poz[y], path[chain[y]].size() - 1));
y = tata[path[chain[y]].back()];
}
}
assert(chain[x] == chain[y]);
int lo = min (poz [x], poz[y]);
int hi = max (poz [x], poz[y]);
return max (result, getMaximum(tree[chain[x]], 1, 0, path[chain[x]].size() - 1, lo, hi));
}
int main() {
ifstream f("heavypath.in");
ofstream g("heavypath.out");
int n, m;
f >> n >> m;
for (int i = 1; i <= n; i++){
f >> values[i];
}
for (int i = 2; i<= n; ++i){
int a, b;
f >> a >> b;
graf[a].push_back(b);
graf[b].push_back(a);
}
dfs (1);
for (int i = 1; i <= numarLanturi; ++ i)
{
tree[i].resize(path[i].size() * 4);
for (int j = 0; j < path [i].size(); ++ j) {
assert (poz[path[i][j]] == j);
update(tree[i], 1, 0, path[i].size() - 1, j, values[path[i][j]]);
}
}
for (int i = 1; i <= m; ++i){
int op, x, y;
f >> op >> x >> y;
if (op == 1)
cout << query(x, y) << '\n';
else {
assert(op == 0);
update(tree[chain[x]], 1, 0, path[chain[x]].size() - 1, poz[x], y);
}
}
return 0;
}