#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#pragma GCC optimize("O3")
#define ll long long
#define ull unsigned long long
#define pii pair<int, int>
#define pb push_back
#define printArr(v, n) for(int i = 0; i < n; i++) cout << v[i] << ' ';
#define readArr(v, n) for(int i = 0; i < n; i++) cin >> v[i];
#define FASTIO cin.tie(0); cout.tie(0); ios_base::sync_with_stdio(0);
#define FILES freopen("heavypath.in", "r", stdin); freopen("heavypath.out", "w", stdout);
using namespace std;
using namespace __gnu_pbds;
template<typename T>
using ordered_set = tree<T,null_type,less<T>, rb_tree_tag,tree_order_statistics_node_update>;
const int NMAX = 1e5 + 5;
struct seg_tree {
int Size;
vector <int> idk;
seg_tree(vector <int> & v) {
Size = v.size();
idk.resize((1 << int(ceil(log2(Size)))) + 1);
init(1, Size, 1, v);
}
int init(int l, int r, int ind, vector <int> & v) {
if(l == r) return idk[ind] = v[l - 1];
int mid = (l + r) / 2;
idk[ind] = max( init(l, mid, ind << 1, v),
init(mid + 1, r, (ind << 1) + 1, v) );
return idk[ind];
}
int query(int L, int R, int ind, int l, int r) {
if(l > R || L > r) return INT_MIN;
if(l >= L && r <= R) return idk[ind];
int mid = (l + r) / 2;
return max( query(L, R, ind << 1, l, mid),
query(L, R, (ind << 1) + 1, mid + 1, r) );
}
void update(int pos, int val, int ind, int l, int r) {
if(l == r) {
idk[ind] = val;
return;
}
int mid = (l + r) / 2;
if(pos <= mid)
update(pos, val, ind << 1, l, mid);
else
update(pos, val, (ind << 1) + 1, mid + 1, r);
idk[ind] = max(idk[ind << 1], idk[(ind << 1) + 1]);
}
};
vector <int> path[NMAX], adj[NMAX];
seg_tree * Seg[NMAX];
int value[NMAX], head[NMAX], depth[NMAX],
sub_tree[NMAX], parent[NMAX], pathOf[NMAX],
pos[NMAX];
int init(int node, int dad = 0) {
parent[node] = dad;
for(int kid : adj[node])
if(kid != dad)
sub_tree[node] += init(kid, node) + 1;
return sub_tree[node];
}
int cnt = 1;
void decompose(int node, int component, bool heavy = 0, int height = 0) {
path[component].pb(value[node]);
pathOf[node] = component;
pos[node] = path[component].size() - 1;
depth[node] = height;
if(!heavy)
head[node] = node;
else
head[node] = head[parent[node]];
for(int kid : adj[node])
if(kid != parent[node]) {
if(sub_tree[kid] >= sub_tree[node] / 2 + sub_tree[node] % 2)
decompose(kid, component, 1, height);
else
decompose(kid, ++cnt, 0, height + 1);
}
}
int query(int x, int y) {
int mx = INT_MIN;
for(; head[x] != head[y]; x = parent[head[x]]) {
if(depth[x] < depth[y])
swap(x, y);
int P = pathOf[x];
mx = max(mx, Seg[P]->query(pos[head[x]] + 1, pos[x] + 1,
1, 1, path[P].size()));
}
int l = min(pos[x], pos[y]);
int r = max(pos[x], pos[y]);
int P = pathOf[x];
return max(mx, Seg[P]->query(l + 1, r + 1, 1, 1, path[P].size()));
}
void update(int node, int val) {
int P = pathOf[node];
Seg[P]->update(pos[node] + 1, val, 1, 1, path[P].size());
}
signed main() {
FASTIO;
#ifndef ONLINE_JUDGE
FILES;
#endif // ONLINE_JUDGE
int n, q; cin >> n >> q;
for(int i = 1; i <= n; i++)
cin >> value[i];
for(int i = 0; i < n - 1; i++) {
int x, y;
cin >> x >> y;
adj[x].pb(y);
adj[y].pb(x);
}
init(1);
decompose(1, cnt, 0, 0);
for(int i = 1; i <= cnt; i++)
Seg[i] = new seg_tree(path[i]);
while(q--) {
int t, x, y;
cin >> t >> x >> y;
if(t == 0) {
update(x, y);
} else {
cout << query(x, y) << '\n';
}
}
return 0;
}