#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
struct SegmentTree {
private:
std::vector<int> tree;
int size;
void update(int node, int left, int right, int pos, int value) {
if (left == right) {
tree[node] = value;
} else {
int mid = (left + right) / 2;
if (pos <= mid) {
update(2 * node, left, mid, pos, value);
} else {
update(2 * node + 1, mid + 1, right, pos, value);
}
tree[node] = std::max(tree[2 * node], tree[2 * node + 1]);
}
}
int query(int node, int left, int right, int start, int end) {
if (start > end || left > right) return 0;
if (start <= left && right <= end) return tree[node];
if (end < left || right < start) return 0;
int mid = (left + right) / 2;
int left_val = query(2 * node, left, mid, start, end);
int right_val = query(2 * node + 1, mid + 1, right, start, end);
return std::max(left_val, right_val);
}
public:
explicit SegmentTree(int n) : size(n) {
tree.resize(4 * n);
}
void update(int pos, int value) {
update(1, 1, size, pos, value);
}
int query(int start, int end) {
if (start > end) std::swap(start, end);
return query(1, 1, size, start, end);
}
};
struct Graph {
std::vector<std::vector<int>> adj;
std::vector<int> value;
std::vector<int> parent, depth, heavy, pos, subtree;
int ptr = 0;
SegmentTree tree;
explicit Graph(std::vector<int>& values) : value(values), tree(values.size() - 1) {
int n = values.size() - 1;
adj.resize(n + 1);
parent.resize(n + 1);
depth.resize(n + 1);
heavy.assign(n + 1, -1);
pos.resize(n + 1);
subtree.resize(n + 1);
}
void add_edge(int x, int y) {
adj[x].push_back(y);
adj[y].push_back(x);
}
int dfs(int node, int par) {
parent[node] = par;
subtree[node] = 1;
int max_subtree = 0;
for (int child : adj[node]) {
if (child != par) {
depth[child] = depth[node] + 1;
int child_subtree = dfs(child, node);
subtree[node] += child_subtree;
if (child_subtree > max_subtree) {
max_subtree = child_subtree;
heavy[node] = child;
}
}
}
return subtree[node];
}
void decompose(int node, int head) {
pos[node] = ++ptr;
tree.update(ptr, value[node]);
if (heavy[node] != -1) {
decompose(heavy[node], head);
}
for (int child : adj[node]) {
if (child != parent[node] && child != heavy[node]) {
decompose(child, child);
}
}
}
void heavy_light() {
dfs(1, -1);
decompose(1, 1);
}
void update(int node, int val) {
tree.update(pos[node], val);
}
int query_path(int u, int v) {
int result = 0;
for (; pos[u] != pos[v]; v = parent[heavy[v]]) {
if (depth[heavy[v]] > depth[heavy[u]]) std::swap(u, v);
if (heavy[u] != -1 && depth[heavy[u]] >= depth[v]) {
result = std::max(result, tree.query(pos[heavy[u]], pos[u]));
u = parent[heavy[u]];
} else {
result = std::max(result, tree.query(pos[v], pos[v]));
v = parent[v];
}
}
if (depth[u] > depth[v]) std::swap(u, v);
result = std::max(result, tree.query(pos[u], pos[v]));
return result;
}
};
int main() {
std::ifstream input("heavypath.in");
std::ofstream output("heavypath.out");
int n, q;
input >> n >> q;
std::vector<int> values(n + 1);
for (int i = 1; i <= n; ++i) {
input >> values[i];
}
Graph graph(values);
for (int i = 0; i < n - 1; ++i) {
int x, y;
input >> x >> y;
graph.add_edge(x, y);
}
graph.heavy_light();
while (q--) {
int type, x, y;
input >> type >> x >> y;
if (type == 0) {
graph.update(x, y);
} else {
output << graph.query_path(x, y) << '\n';
}
}
return 0;
}