#ifdef LOCAL
#include <iostream>
#include <fstream>
#else
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#define endl '\n'
#endif
#define all(a) (a).begin(), (a).end()
#define sz(a) ((int)(a).size())
#define fi first
#define se second
#define lsb(x) (x & (-x))
using namespace std;
template <typename T>
bool ckmax(T &a, T b) { return a < b ? a = b, true : false; }
template <typename T>
bool ckmin(T &a, T b) { return a > b ? a = b, true : false; }
typedef long long ll;
typedef pair<int, int> pii;
const int NMAX = 1e5+5;
#ifdef LOCAL
ifstream fin("input.txt");
#define fout cout
#else
ifstream fin("heavypath.in");
ofstream fout("heavypath.out");
#endif
struct SegTree {
int n;
vector<int> aint;
SegTree() { }
SegTree(int _n): n(_n), aint(4 * (_n + 5)) { }
void update(int node, int l, int r, int pos, int val) {
if (l >= r) {
aint[node] = val;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid)
update(node * 2, l, mid, pos, val);
else
update(node * 2 + 1, mid + 1, r, pos, val);
aint[node] = max(aint[node * 2], aint[node * 2 + 1]);
}
void update(int pos, int val) {
update(1, 1, n, pos, val);
}
int query(int node, int l, int r, int ll, int rr) {
if (ll <= l && r <= rr)
return aint[node];
int mid = (l + r) >> 1;
int mx = 0;
if (ll <= mid)
mx = query(node * 2, l, mid, ll, rr);
if (mid < rr)
ckmax(mx, query(node * 2 + 1, mid + 1, r, ll, rr));
return mx;
}
int query(int l, int r) {
return query(1, 1, n, l, r);
}
};
int n, q;
int a[NMAX];
vector<int> g[NMAX];
int daddy[NMAX], depth[NMAX];
int heavy[NMAX], head[NMAX], pos[NMAX];
SegTree seg_tree;
int dfs(int x = 1) {
int size = 1;
int mx = 0;
for (auto &it : g[x]) {
if (it == daddy[x])
continue;
daddy[it] = x;
depth[it] = depth[x] + 1;
int child_size = dfs(it);
size += child_size;
if (ckmax(mx, child_size))
heavy[x] = it;
}
return size;
}
void heavy_decomp(int x = 1, int h = 1) {
static int cnt = 0;
head[x] = h;
pos[x] = ++cnt;
if (heavy[x])
heavy_decomp(heavy[x], h);
for (auto &it : g[x]) {
if (it == daddy[x] || it == heavy[x])
continue;
heavy_decomp(it, it);
}
}
void update(int x, int y) {
seg_tree.update(pos[x], y);
}
int query(int x, int y) {
int ans = 0;
while (head[x] != head[y]) {
if (depth[head[x]] < depth[head[y]])
swap(x, y);
ans = max(ans, seg_tree.query(pos[head[x]], pos[x]));
x = daddy[head[x]];
}
if (depth[x] < depth[y])
swap(x, y);
return max(ans, seg_tree.query(pos[y], pos[x]));
}
signed main() {
fin >> n >> q;
for (int i = 1; i <= n; i++)
fin >> a[i];
for (int i = 1, x, y; i < n; i++) {
fin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
dfs();
heavy_decomp();
daddy[1] = 1;
seg_tree = SegTree(n);
for (int i = 1; i <= n; i++)
seg_tree.update(pos[i], a[i]);
while (q--) {
int t, x, y;
fin >> t >> x >> y;
if (t == 0) update(x, y);
else fout << query(x, y) << endl;
}
return 0;
}