#include <cstdio>
#include <cassert>
#include <vector>
#include <algorithm>
using namespace std;
class SegmentTree {
public:
SegmentTree(const vector<int> &values):
size(1),
tree(vector<int>()) {
for (; size < int(values.size()); size *= 2);
tree = vector<int>(2 * size, 0);
for (int x = 0; x < int(values.size()); ++x)
tree[x + size] = values[x];
for (int x = size - 1; x > 0; --x)
tree[x] = max(tree[2 * x], tree[2 * x + 1]);
}
void Update(int where, const int value) {
tree[where += size] = value;
for (where /= 2; where > 0; where /= 2)
tree[where] = max(tree[2 * where], tree[2 * where + 1]);
}
int Query(int from, int to) const {
int answer = 0;
from += size;
to += size;
while (from <= to) {
answer = max(answer, max(tree[from], tree[to]));
from = (from + 1) / 2;
to = (to - 1) / 2;
}
return answer;
}
private:
int size;
vector<int> tree;
};
class Tree {
public:
static const int NIL = -1;
Tree():
v(0),
root(NIL),
edges(vector< vector<int> >()),
father(vector<int>()),
size(vector<int>()),
level(vector<int>()) {}
Tree(const int _v, const int _root, const vector< pair<int, int> > &_edges):
v(_v),
root(_root),
edges(vector< vector<int> >(_v, vector<int>())),
father(vector<int>(_v, NIL)),
size(vector<int>(_v, 0)),
level(vector<int>(_v, NIL)) {
for (int i = 0; i < int(_edges.size()); ++i) {
edges[_edges[i].first].push_back(_edges[i].second);
edges[_edges[i].second].push_back(_edges[i].first);
}
father[root] = NIL;
level[root] = 0;
InitializeDFS(root);
}
int V() const {
return v;
}
int Root() const {
return root;
}
vector<int>::const_iterator EdgesBegin(const int x) const {
return edges[x].begin();
}
vector<int>::const_iterator EdgesEnd(const int x) const {
return edges[x].end();
}
int Father(const int x) const {
return father[x];
}
int Size(const int x) const {
return size[x];
}
int Level(const int x) const {
return level[x];
}
void GetHeavyPathDecomposition(vector< vector<int> > &paths, vector<int> &pathIndex, vector<int> &pathPosition) const {
pathIndex = pathPosition = vector<int>(v, NIL);
DFS(root, paths, pathIndex);
for (int i = 0; i < int(paths.size()); ++i) {
reverse(paths[i].begin(), paths[i].end());
for (int j = 0; j < int(paths[i].size()); ++j)
pathPosition[paths[i][j]] = j;
}
}
vector< vector<int> > GetHeavyPathDecomposition() const {
vector< vector<int> > paths;
vector<int> pathIndex, pathPosition;
GetHeavyPathDecomposition(paths, pathIndex, pathPosition);
return paths;
}
private:
int v, root;
vector< vector<int> > edges;
vector<int> father, size, level;
void InitializeDFS(const int x) {
size[x] = 1;
for (vector<int>::const_iterator y = edges[x].begin(); y != edges[x].end(); ++y) {
if (*y != father[x]) {
father[*y] = x;
level[*y] = level[x] + 1;
InitializeDFS(*y);
size[x] += size[*y];
}
}
}
void DFS(const int x, vector< vector<int> > &paths, vector<int> &pathIndex) const {
int heaviestSon = NIL;
for (vector<int>::const_iterator y = edges[x].begin(); y != edges[x].end(); ++y) {
if (*y != father[x]) {
DFS(*y, paths, pathIndex);
if (heaviestSon == NIL || size[*y] > size[heaviestSon])
heaviestSon = *y;
}
}
if (heaviestSon == NIL) {
pathIndex[x] = int(paths.size());
paths.push_back(vector<int>());
} else {
pathIndex[x] = pathIndex[heaviestSon];
}
paths[pathIndex[x]].push_back(x);
}
};
Tree T;
vector< vector<int> > Paths;
vector<int> Values, PathIndex, PathPosition;
vector<SegmentTree> SegmentTrees;
int Q;
inline int QueryPath(int from, int to) {
if (PathIndex[from] == PathIndex[to]) {
if (T.Level(from) > T.Level(to))
swap(from, to);
return SegmentTrees[PathIndex[from]].Query(PathPosition[from], PathPosition[to]);
}
if (T.Level(Paths[PathIndex[from]].front()) > T.Level(Paths[PathIndex[to]].front()))
swap(from, to);
return max(SegmentTrees[PathIndex[to]].Query(0, PathPosition[to]), QueryPath(from, T.Father(Paths[PathIndex[to]].front())));
}
void Solve() {
T.GetHeavyPathDecomposition(Paths, PathIndex, PathPosition);
for (int i = 0; i < int(Paths.size()); ++i) {
vector<int> pathValues = vector<int>(int(Paths[i].size()));
for (int j = 0; j < int(Paths[i].size()); ++j)
pathValues[j] = Values[Paths[i][j]];
SegmentTrees.push_back(SegmentTree(pathValues));
}
for (; Q > 0; --Q) {
int type;
assert(scanf("%d", &type) == 1);
if (type == 0) {
int node, value;
assert(scanf("%d %d", &node, &value) == 2);
--node;
SegmentTrees[PathIndex[node]].Update(PathPosition[node], Values[node] = value);
} else {
int from, to;
assert(scanf("%d %d", &from, &to) == 2);
printf("%d\n", QueryPath(--from, --to));
}
}
}
void Read() {
int v;
assert(scanf("%d %d", &v, &Q) == 2);
Values = vector<int>(v);
for (int x = 0; x < v; ++x)
assert(scanf("%d", &Values[x]) == 1);
vector< pair<int, int> > edges;
for (int i = 1; i < v; ++i) {
int x, y;
assert(scanf("%d %d", &x, &y) == 2);
edges.push_back(make_pair(--x, --y));
}
T = Tree(v, 0, edges);
}
int main() {
assert(freopen("heavypath.in", "r", stdin));
assert(freopen("heavypath.out", "w", stdout));
Read();
Solve();
return 0;
}