#include <vector>
#include <fstream>
using std::vector;
std::ifstream fin("heavypath.in");
std::ofstream fout("heavypath.out");
inline int min(int x, int y) {
return x < y ? x : y;
}
inline int max(int x, int y) {
return x > y ? x : y;
}
class SegmTree {
private:
int n;
vector<int> tree;
void build(int node, int left, int right, vector<int>& v) {
if (left == right) {
tree[node] = v[left];
return;
}
int mid = (left + right) / 2;
build(2 * node, left, mid, v);
build(2 * node + 1, mid + 1, right, v);
tree[node] = max(tree[2 * node], tree[2 * node + 1]);
}
void update(int node, int left, int right, int pos, int val) {
if (left == right) {
tree[node] = val;
return;
}
int mid = (left + right) / 2;
if (pos <= mid)
update(2 * node, left, mid, pos, val);
else
update(2 * node + 1, mid + 1, right, pos, val);
tree[node] = max(tree[2 * node], tree[2 * node + 1]);
}
int query(int node, int left, int right, int qLeft, int qRight) {
if (left == qLeft && right == qRight)
return tree[node];
int mid = (left + right) / 2;
if (qRight <= mid)
return query(2 * node, left, mid, qLeft, qRight);
if (qLeft > mid)
return query(2 * node + 1, mid + 1, right, qLeft, qRight);
return max(
query(2 * node, left, mid, qLeft, mid),
query(2 * node + 1, mid + 1, right, mid + 1, qRight)
);
}
public:
void init(vector<int>& v) {
n = v.size();
tree.resize(4 * n);
build(1, 0, n - 1, v);
}
void update(int pos, int val) {
update(1, 0, n - 1, pos, val);
}
int query(int left, int right) {
return query(1, 0, n - 1, left, right);
}
};
class Tree {
private:
struct Chain {
int id;
int father;
SegmTree tree;
vector<int> nodes;
Chain(int id) {
this->id = id;
}
};
int n;
vector<int> depth;
vector<vector<int>> ad;
vector<Chain> chains;
vector<int> chain, pos;
void setChainFather(int chain, int father) {
chains[chain].father = father;
}
void addChainNode(int chainID, int node) {
chain[node] = chainID;
pos[node] = chains[chainID].nodes.size();
chains[chainID].nodes.push_back(node);
}
int dfs(int node, int father) {
int card = 1;
depth[node] = depth[father] + 1;
if (ad[node].size() == 1 && node != 1) {
chains.push_back(Chain(chains.size()));
addChainNode(chains.size() - 1, node);
return card;
}
int maxNghb = 0;
int maxCard = 0;
for (int nghb : ad[node])
if (nghb != father) {
int nowCard = dfs(nghb, node);
card += nowCard;
if (nowCard > maxCard) {
maxCard = nowCard;
maxNghb = nghb;
}
}
addChainNode(chain[maxNghb], node);
for (int nghb : ad[node])
if (nghb != father && nghb != maxNghb)
setChainFather(chain[nghb], node);
return card;
}
public:
Tree(int n) {
this->n = n;
ad.resize(n + 1);
}
void addEdge(int x, int y) {
ad[x].push_back(y);
ad[y].push_back(x);
}
void heavyPath(vector<int>& val) {
pos.resize(n + 1);
chain.resize(n + 1);
depth.resize(n + 1);
dfs(1, 0);
for (Chain& chain : chains) {
vector<int> v;
v.reserve(chain.nodes.size());
for (int node : chain.nodes)
v.push_back(val[node]);
chain.tree.init(v);
}
}
void update(int node, int val) {
chains[chain[node]].tree.update(pos[node], val);
}
int query(int x, int y) {
if (chain[x] == chain[y])
return chains[chain[x]].tree.query(min(pos[x], pos[y]), max(pos[x], pos[y]));
if (depth[chains[chain[x]].nodes.back()] < depth[chains[chain[y]].nodes.back()])
return max(chains[chain[y]].tree.query(pos[y], chains[chain[y]].nodes.size() - 1), query(x, chains[chain[y]].father));
return max(chains[chain[x]].tree.query(pos[x], chains[chain[x]].nodes.size() - 1), query(chains[chain[x]].father, y));
}
};
int main() {
int n, q;
fin >> n >> q;
Tree tree(n);
vector<int> val(n + 1);
for (int i = 1; i <= n; i++)
fin >> val[i];
for (int i = 1; i < n; i++) {
int x, y; fin >> x >> y;
tree.addEdge(x, y);
}
tree.heavyPath(val);
for (int i = 0; i < q; i++) {
int t, x, y;
fin >> t >> x >> y;
if (t)
fout << tree.query(x, y) << '\n';
else
tree.update(x, y);
}
fout.close();
return 0;
}