#pragma GCC optimize("O3")
#include <fstream>
#include <vector>
#include <bitset>
#include <cmath>
#include <algorithm>
struct node {
int val, siz;
int lvl;
int pos, root;
std::vector <int> par;
std::vector <int> con;
};
int nrn;
node web[100005];
std::bitset <100005> viz;
int aint[400005];
void update(int node, int posl, int posr, int pos, int val) {
if (posl == posr) {
aint[node] = val;
}
else {
int mid = (posl + posr) / 2;
if (pos <= mid) {
update((node << 1) + 1, posl, mid, pos, val);
}
else {
update((node << 1) + 2, mid + 1, posr, pos, val);
}
aint[node] = std::max(aint[(node << 1) + 1], aint[(node << 1) + 2]);
}
}
int query(int node, int posl, int posr, int pos1, int pos2) {
if (posl == pos1 && posr == pos2) {
return aint[node];
}
else {
int mid = (posl + posr) / 2, ans = 0;
if (pos1 <= mid) {
ans = query((node << 1) + 1, posl, mid, pos1, std::min(pos2, mid));
}
if (mid < pos2) {
ans = std::max(ans, query((node << 1) + 2, mid + 1, posr, std::max(pos1, mid + 1), pos2));
}
return ans;
}
}
void sizdfs(int pos) {
static int lvl = 0;
web[pos].siz = 1;
web[pos].lvl = lvl;
while ((1 << web[pos].par.size()) <= lvl) {
web[pos].par.push_back(web[web[pos].par.back()].par[web[pos].par.size() - 1]);
}
viz[pos] = true;
lvl++;
for (int chd : web[pos].con) {
if (!viz[chd]) {
web[chd].par.push_back(pos);
sizdfs(chd);
web[pos].siz += web[chd].siz;
}
}
lvl--;
}
bool sizcomp(int nr1, int nr2) {
return web[nr1].siz > web[nr2].siz;
}
void aintdfs(int pos) {
static int aintcnt = 1, poslast = 1;
int jump = 0;
std::sort(web[pos].con.begin(), web[pos].con.end(), sizcomp);
viz[pos] = true;
web[pos].pos = aintcnt;
web[pos].root = poslast;
update(0, 1, nrn, aintcnt, web[pos].val);
for (int index = 0; index < web[pos].con.size(); index++) {
if (web[pos].con[index] == web[pos].par[0]) {
jump++;
}
else {
if (jump) {
web[pos].con[index - jump] = web[pos].con[index];
}
aintcnt++;
if (index == jump) {
aintdfs(web[pos].con[index]);
}
else {
poslast = web[pos].con[index];
aintdfs(web[pos].con[index]);
}
}
}
while (jump--) {
web[pos].con.pop_back();
}
}
int lca(int pos1, int pos2) {
for (int index = std::max(web[pos1].par.size(), web[pos2].par.size()); web[pos1].lvl != web[pos2].lvl && index >= 0; index--) {
if (web[pos1].lvl - web[pos2].lvl >= (1 << index)) {
pos1 = web[pos1].par[index];
}
if (web[pos2].lvl - web[pos1].lvl >= (1 << index)) {
pos2 = web[pos2].par[index];
}
}
if (pos1 == pos2) {
return pos1;
}
for (int index = web[pos1].par.size() - 1; index >= 0; index--) {
if (web[pos1].par.size() > index && web[pos1].par[index] != web[pos2].par[index]) {
pos1 = web[pos1].par[index];
pos2 = web[pos2].par[index];
}
}
return web[pos1].par[0];
}
int main() {
std::ifstream fin("heavypath.in");
std::ofstream fout("heavypath.out");
int nrm, pos1, pos2, cer, nrx, nry;
int cpar, ans;
fin >> nrn >> nrm;
for (int index = 1; index <= nrn; index++) {
fin >> web[index].val;
}
for (int index = 0; index < nrn - 1; index++) {
fin >> pos1 >> pos2;
web[pos1].con.push_back(pos2);
web[pos2].con.push_back(pos1);
}
web[1].par.push_back(-1);
viz.reset();
sizdfs(1);
viz.reset();
aintdfs(1);
for (int index = 0; index < nrm; index++) {
fin >> cer >> nrx >> nry;
if (cer) {
cpar = lca(nrx, nry);
ans = 0;
while (web[web[nrx].root].lvl > web[cpar].lvl) {
ans = std::max(ans, query(0, 1, nrn, web[web[nrx].root].pos, web[nrx].pos));
nrx = web[web[nrx].root].par[0];
}
ans = std::max(ans, query(0, 1, nrn, web[cpar].pos, web[nrx].pos));
while (web[web[nry].root].lvl > web[cpar].lvl) {
ans = std::max(ans, query(0, 1, nrn, web[web[nry].root].pos, web[nry].pos));
nry = web[web[nry].root].par[0];
}
ans = std::max(ans, query(0, 1, nrn, web[cpar].pos, web[nry].pos));
fout << ans << '\n';
}
else {
update(0, 1, nrn, web[nrx].pos, nry);
}
}
}