#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int LOGN = 16;
const int NMAX = 100000;
class aint {
private:
vector<int> maxim;
void upd(int nod, int nst, int ndr, int upoz, int val) {
if(nst == ndr)
maxim[nod] = val;
else {
int mij = (nst + ndr) / 2;
if(upoz <= mij)
upd(2 * nod, nst, mij, upoz, val);
else
upd(2 * nod + 1, mij + 1, ndr, upoz, val);
maxim[nod] = max(maxim[2 * nod], maxim[2 * nod + 1]);
}
}
int qry(int nod, int nst, int ndr, int qst, int qdr) {
if(qst > ndr || qdr < nst) return 0;
if(nst >= qst && ndr <= qdr) return maxim[nod];
int mij = (nst + ndr) / 2;
return max(qry(2 * nod, nst, mij, qst, qdr), qry(2 * nod + 1, mij + 1, ndr, qst, qdr));
}
public:
int sz;
void init(vector<int> &v, int nod, int nst, int ndr) {
if(nst == ndr)
maxim[nod] = v[nst];
else {
int mij = (nst + ndr) / 2;
init(v, 2 * nod, nst, mij);
init(v, 2 * nod + 1, mij + 1, ndr);
maxim[nod] = max(maxim[2 * nod], maxim[2 * nod + 1]);
}
}
aint() {
sz = 0;
}
aint(vector<int> &v) {
sz = v.size();
maxim.resize(4 * v.size());
init(v, 1, 0, v.size() - 1);
}
void update(int poz, int val) {
upd(1, 0, sz - 1, poz, val);
}
int query(int qst) {
return qry(1, 0, sz - 1, qst, sz - 1);
}
int query(int qst, int qdr) {
return qry(1, 0, sz - 1, qst, qdr);
}
};
int n, m, euler_idx;
int val[NMAX + 5], first_ap[NMAX + 5], h[NMAX + 5];
int path[NMAX + 5], path_poz[NMAX + 5];
int rmq[LOGN][2 * NMAX + 5];
vector<int> arb[NMAX + 5], path_dad;
vector<aint> aints;
vector<vector<int>> paths;
void dfs(int nod, int dad) {
h[nod] = h[dad] + 1;
first_ap[nod] = ++euler_idx;
rmq[0][euler_idx] = nod;
for(int vec: arb[nod])
if(vec != dad) {
dfs(vec, nod);
rmq[0][++euler_idx] = nod;
}
}
int get_log2(int x) {
int log2 = 0;
while(x > 1) x >>= 1, log2++;
return log2;
}
void calc_rmq() {
euler_idx = 0;
dfs(1, 0);
for(int l = 2; l <= euler_idx; l *= 2)
for(int i = 1; i + l - 1 <= euler_idx; i++) {
int log2 = get_log2(l), nod_st = rmq[log2 - 1][i], nod_dr = rmq[log2 - 1][i + l / 2];
rmq[log2][i] = (h[nod_st] < h[nod_dr]) ? nod_st : nod_dr;
}
}
int get_lca(int nod1, int nod2) {
int f1 = first_ap[nod1], f2 = first_ap[nod2];
if(f1 > f2) swap(f1, f2);
int l = f2 - f1 + 1, log2 = get_log2(l);
int nst = rmq[log2][f1], ndr = rmq[log2][f2 - l + 1];
return (h[nst] < h[ndr]) ? nst : ndr;
}
int path_decomposition(int nod, int dad) {
bool frunza = true;
int fii = 1, max_fii = 0, heaviest = -1;
for(int vec: arb[nod])
if(vec != dad) {
frunza = false;
int fii_vec = path_decomposition(vec, nod);
fii += fii_vec;
if(fii_vec > max_fii) {
max_fii = fii_vec;
heaviest = path[vec];
}
}
if(!frunza) {
paths[heaviest].push_back(nod);
path[nod] = heaviest;
path_poz[nod] = paths[heaviest].size() - 1;
for(int vec: arb[nod])
if(vec != dad && path[vec] != heaviest)
path_dad[path[vec]] = nod;
}
else {
vector<int> new_path;
paths.push_back(new_path);
paths.back().push_back(nod);
path_dad.push_back(0);
path[nod] = paths.size() - 1;
path_poz[nod] = 0;
}
return fii;
}
int query(int nod1, int nod2) {
int ans = 0, lca = get_lca(nod1, nod2);
while(path[nod1] != path[lca]) {
ans = max(ans, aints[path[nod1]].query(path_poz[nod1]));
nod1 = path_dad[path[nod1]];
}
while(path[nod2] != path[lca]) {
ans = max(ans, aints[path[nod2]].query(path_poz[nod2]));
nod2 = path_dad[path[nod2]];
}
int p = path[lca], poz1 = path_poz[nod1], poz2 = path_poz[nod2];
if(poz1 > poz2) swap(poz1, poz2);
ans = max(ans, aints[p].query(poz1, poz2));
return ans;
}
int main() {
freopen("heavypath.in", "r", stdin);
freopen("heavypath.out", "w", stdout);
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++)
scanf("%d", &val[i]);
for(int i = 1; i < n; i++) {
int x, y;
scanf("%d %d", &x, &y);
arb[x].push_back(y);
arb[y].push_back(x);
}
calc_rmq();
path_decomposition(1, 0);
for(int i = 0; i < paths.size(); i++) {
vector<int> vpath;
for(int crt: paths[i])
vpath.push_back(val[crt]);
aint new_aint = aint(vpath);
aints.push_back(new_aint);
}
for(int i = 1; i <= m; i++) {
int t, x, y;
scanf("%d %d %d", &t, &x, &y);
if(t == 0)
aints[path[x]].update(path_poz[x], y);
else
printf("%d\n", query(x, y));
}
return 0;
}