#include <fstream>
#include <algorithm>
#include <vector>
using namespace std;
typedef vector<int>::iterator it;
const int MAX_N = 100005;
const int oo = 0x3f3f3f3f;
const int NIL = -1;
class SegmentTree {
public:
SegmentTree() {
size = 0;
tree = vector<int>();
}
SegmentTree(vector<int> &values) {
size = static_cast<int>(values.size());
int capacity = 1;
for (; capacity <= size; capacity *= 2);
tree = vector<int>(2 * capacity + 1);
BuildTree(1, 0, size - 1, values);
}
void Update(int position, int value) {
Update(1, 0, size - 1, position, value);
}
int Query(int left, int right) {
return Query(1, 0, size - 1, left, right);
}
private:
int size;
vector<int> tree;
void BuildTree(int index, int left, int right, vector<int> &values) {
int middle = (left + right) / 2;
if (left == right) {
tree[index] = values[middle];
return;
}
BuildTree(2 * index, left, middle, values);
BuildTree(2 * index + 1, middle + 1, right, values);
tree[index] = max(tree[2 * index], tree[2 * index + 1]);
}
void Update(int index, int left, int right, int position, int value) {
int middle = (left + right) / 2;
if (left == right) {
tree[index] = value;
return;
}
if (position <= middle)
Update(2 * index, left, middle, position, value);
else
Update(2 * index + 1, middle + 1, right, position, value);
tree[index] = max(tree[2 * index], tree[2 * index + 1]);
}
int Query(int index, int left, int right, int qLeft, int qRight) {
int middle = (left + right) / 2;
if (qLeft <= left && right <= qRight)
return tree[index];
int answer = -oo;
if (qLeft <= middle)
answer = max(answer, Query(2 * index, left, middle, qLeft, qRight));
if (middle < qRight)
answer = max(answer, Query(2 * index + 1, middle + 1, right, qLeft, qRight));
return answer;
}
};
vector<int> Tree[MAX_N];
int V, Values[MAX_N], Father[MAX_N], Level[MAX_N], Weight[MAX_N];
int Path[MAX_N], Length[MAX_N], Position[MAX_N], Begin[MAX_N];
vector<SegmentTree> SegTrees;
int Q;
void DFS(int x) {
Weight[x] = 1;
int heaviestSon = NIL;
for (it y = Tree[x].begin(); y != Tree[x].end(); ++y) {
if (*y == Father[x])
continue;
Father[*y] = x;
Level[*y] = Level[x] + 1;
DFS(*y);
Weight[x] += Weight[*y];
if (heaviestSon == NIL || Weight[*y] > Weight[heaviestSon])
heaviestSon = *y;
}
if (heaviestSon == NIL)
Path[x] = Path[0]++;
else
Path[x] = Path[heaviestSon];
Position[x] = ++Length[Path[x]];
}
void BuildHeavyPathDecomposition() {
Level[1] = 0;
DFS(1);
for (int x = 1; x <= V; ++x) {
Position[x] = Length[Path[x]] - Position[x];
if (Position[x] == 0)
Begin[Path[x]] = x;
}
}
void BuildSegmentTrees() {
vector< vector<int> > pathValues;
pathValues.resize(Path[0]);
for (int p = 0; p < Path[0]; ++p)
pathValues[p].resize(Length[p]);
for (int x = 1; x <= V; ++x)
pathValues[Path[x]][Position[x]] = Values[x];
for (int p = 0; p < Path[0]; ++p)
SegTrees.push_back(SegmentTree(pathValues[p]));
}
int QueryPath(int x, int y) {
if (Path[x] == Path[y])
return SegTrees[Path[x]].Query(min(Position[x], Position[y]), max(Position[x], Position[y]));
if (Level[Begin[Path[x]]] < Level[Begin[Path[y]]])
swap(x, y);
return max(SegTrees[Path[x]].Query(0, Position[x]), QueryPath(Father[Begin[Path[x]]], y));
}
void ReadTree(ifstream &in) {
in >> V >> Q;
for (int x = 1; x <= V; ++x)
in >> Values[x];
for (int i = 1; i < V; ++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);
BuildHeavyPathDecomposition();
BuildSegmentTrees();
for (; Q > 0; --Q) {
int type, x, y;
in >> type >> x >> y;
if (type == 0)
SegTrees[Path[x]].Update(Position[x], y);
if (type == 1)
out << QueryPath(x, y) << "\n";
}
in.close();
out.close();
return 0;
}