#ifdef LOCAL
#include <debug.hpp>
#else
#pragma comment(linker, "/stack:200000000")
#pragma GCC optimize("Ofast")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,tune=native")
#define debug(...)
#endif
#include <bits/stdc++.h>
using namespace std;
struct SegTree {
vector<int> T; int n;
SegTree(int n) : T(2 * n, (int)-2e9), n(n) {}
void Update(int pos, int val) {
for (T[pos += n] = val; pos > 1; pos /= 2)
T[pos / 2] = max(T[pos], T[pos ^ 1]);
}
int Query(int b, int e) {
int res = -2e9;
for (b += n, e += n; b < e; b /= 2, e /= 2) {
if (b % 2) res = max(res, T[b++]);
if (e % 2) res = max(res, T[--e]);
}
return res;
}
};
struct HeavyLight {
vector<int> jump, sub, depth, enter, parent;
vector<vector<int>> graph;
int timer = 0;
HeavyLight(int n) :
jump(n), sub(n), depth(n),
enter(n), parent(n), graph(n) {}
void AddEdge(int a, int b) {
graph[a].push_back(b);
graph[b].push_back(a);
}
void Build(int root) {
dfs1(root, -1, 0); dfs2(root, root);
}
// Returns the position in the HL linearization
int GetPos(int node) {
assert(timer); // Call Build() before
return enter[node];
}
// Computes an array of ranges of form [li...ri)
// that correspond to the ranges you would need
// to query in the underlying structure
void GetRanges(int a, int b, vector<pair<int, int>>& ret) {
assert(timer); // Call Build() before
while (jump[a] != jump[b]) {
if (depth[jump[a]] < depth[jump[b]]) swap(a, b);
ret.emplace_back(enter[jump[a]], enter[a] + 1);
a = parent[jump[a]];
}
if (depth[a] < depth[b]) swap(a, b);
ret.emplace_back(enter[b], enter[a] + 1);
}
int dfs1(int node, int par, int dep) {
sub[node] = 1; parent[node] = par; depth[node] = dep;
if (par != -1)
graph[node].erase(
find(graph[node].begin(), graph[node].end(), par));
for (auto vec : graph[node])
sub[node] += dfs1(vec, node, dep + 1);
return sub[node];
}
void dfs2(int node, int jmp) {
jump[node] = jmp; enter[node] = timer++;
iter_swap(graph[node].begin(), max_element(
graph[node].begin(), graph[node].end(),
[&](int a, int b) { return sub[a] < sub[b]; }
));
for (auto vec : graph[node])
dfs2(vec, vec == graph[node].front() ? jmp : vec);
}
};
int main() {
ifstream cin("heavypath.in");
ofstream cout("heavypath.out");
int n, q; cin >> n >> q;
vector<int> v(n);
for (int i = 0; i < n; ++i)
cin >> v[i];
HeavyLight H(n);
for (int i = 1; i < n; ++i) {
int a, b; cin >> a >> b;
H.AddEdge(a - 1, b - 1);
}
H.Build(0);
SegTree st(n);
for (int i = 0; i < n; ++i)
st.Update(H.GetPos(i), v[i]);
vector<pair<int, int>> ranges;
for (int i = 0; i < q; ++i) {
int t, a, b; cin >> t >> a >> b;
if (t == 0) st.Update(H.GetPos(a - 1), b);
else {
H.GetRanges(a - 1, b - 1, ranges);
int ans = -2e9;
for (auto itr : ranges)
ans = max(ans, st.Query(itr.first, itr.second));
cout << ans << '\n';
ranges.clear();
}
}
return 0;
}