#include <cstring>
#include <fstream>
#include <vector>
using namespace std;
typedef vector<int>::iterator it;
const int MAX_N = 100005;
const int NIL = -1;
const int oo = 0x3f3f3f3f;
class SegmentTree {
public:
SegmentTree(const vector<int>& values) {
length = static_cast<int>(values.size());
for (size = 1; size < length; size *= 2);
tree = vector<int>(2 * size + 1);
BuildTree(1, 0, length - 1, values);
}
void Update(int position, int value) {
Update(1, 0, length - 1, position, value);
}
int Query(int qleft, int qright) {
return Query(1, 0, length - 1, qleft, qright);
}
private:
int size, length;
vector<int> tree;
void BuildTree(int node, int left, int right, const vector<int>& values) {
int middle = (left + right) / 2;
if (left == right) {
tree[node] = values[middle];
return;
}
BuildTree(2 * node, left, middle, values);
BuildTree(2 * node + 1, middle + 1, right, values);
tree[node] = max(tree[2 * node], tree[2 * node + 1]);
}
void Update(int node, int left, int right, int position, int value) {
int middle = (left + right) / 2;
if (left == right) {
tree[node] = value;
return;
}
if (position <= middle)
Update(2 * node, left, middle, position, value);
else
Update(2 * node + 1, middle + 1, right, position, value);
tree[node] = max(tree[2 * node], tree[2 * node + 1]);
}
int Query(int node, int left, int right, int qleft, int qright) {
int middle = (left + right) / 2;
if (qleft <= left && right <= qright)
return tree[node];
int answer = -oo;
if (qleft <= middle)
answer = max(answer, Query(2 * node, left, middle, qleft, qright));
if (middle < qright)
answer = max(answer, Query(2 * node + 1, middle + 1, right, qleft, qright));
return answer;
}
};
vector<int> Tree[MAX_N];
int N, Father[MAX_N], Level[MAX_N], Size[MAX_N], Begin[MAX_N], Path[MAX_N], Position[MAX_N], Length[MAX_N];
int Values[MAX_N], Q;
vector<SegmentTree> SegTrees;
void DFS(int X) {
Size[X] = 1;
int HeaviestSon = NIL;
for (it Y = Tree[X].begin(); Y != Tree[X].end(); ++Y) {
if (Father[*Y] == NIL) {
Father[*Y] = X; Level[*Y] = Level[X] + 1;
DFS(*Y);
Size[X] += Size[*Y];
if (HeaviestSon == NIL || Size[HeaviestSon] < Size[*Y])
HeaviestSon = *Y;
}
}
if (HeaviestSon == NIL)
Path[X] = Path[0]++;
else
Path[X] = Path[HeaviestSon];
Position[X] = Length[Path[X]]++;
}
void BuildHeavyPathDecomposition() {
memset(Father, NIL, sizeof(Father));
Father[1] = 1; Level[1] = 0;
DFS(1);
for (int X = 1; X <= N; ++X) {
Position[X] = Length[Path[X]] - 1 - Position[X];
if (Position[X] == 0)
Begin[Path[X]] = X;
}
}
void BuildSegmentTrees() {
vector< vector<int> > PathValues(Path[0]);
for (int i = 0; i < Path[0]; ++i)
PathValues[i] = vector<int>(Length[i]);
for (int X = 1; X <= N; ++X)
PathValues[Path[X]][Position[X]] = Values[X];
for (int i = 0; i < Path[0]; ++i)
SegTrees.push_back(SegmentTree(PathValues[i]));
}
int QueryPath(int X, int Y) {
if (Level[X] < Level[Y])
swap(X, Y);
if (Path[X] == Path[Y])
return SegTrees[Path[X]].Query(Position[Y], Position[X]);
return max(SegTrees[Path[X]].Query(0, Position[X]), QueryPath(Father[Begin[Path[X]]], Y));
}
void Solve(ifstream& in, ofstream& out) {
BuildHeavyPathDecomposition();
BuildSegmentTrees();
for (; Q > 0; --Q) {
int Type, X, Y; in >> Type >> X >> Y;
if (Type == 0)
SegTrees[Path[X]].Update(Position[X], Y);
else
out << QueryPath(X, Y) << "\n";
}
}
void ReadTree(ifstream& in) {
in >> N >> Q;
for (int i = 1; i <= N; ++i)
in >> Values[i];
for (int i = 1; i < N; ++i) {
int X, Y; in >> X >> Y;
Tree[X].push_back(Y); Tree[Y].push_back(X);
}
}
int main() {
ifstream in("heavypath.in");
ofstream out("heavypath.out");
ReadTree(in);
Solve(in, out);
in.close();
out.close();
return 0;
}