#include <fstream>
#include <vector>
#include <cassert>
#include <cmath>
using namespace std;
const int NMAX = 100005;
ifstream cin ("heavypath.in");
ofstream cout ("heavypath.out");
int cost[NMAX], cul[NMAX], sub[NMAX], nivel[NMAX], f[NMAX], order[NMAX];
vector <int> g[NMAX];
vector < vector <int> > lant;
vector < vector <int> > tree;
void dfs(int node, int father, int lvl)
{
sub[node] = 1;
nivel[node] = lvl;
f[node] = father;
for(auto x: g[node]) {
if(x != father) {
dfs(x, node, lvl + 1);
sub[node] += sub[x];
}
}
if(sub[node] == 1) {
lant.push_back({node});
cul[node] = lant.size() - 1;
} else {
int maxim_size = 0;
for(auto x: g[node]) {
if(x == father)
continue;
if(sub[x] > sub[maxim_size]) {
maxim_size = x;
}
}
lant[cul[maxim_size]].push_back(node);
cul[node] = cul[maxim_size];
}
}
void update_tree(vector <int> &currTree, int node, int st, int dr, int target, int val)
{
if(st == dr) {
currTree[node] = val;
return;
}
int med = (st + dr) / 2;
if(target <= med)
update_tree(currTree, node * 2, st, med, target, val);
else
update_tree(currTree, node * 2 + 1, med + 1, dr, target, val);
currTree[node] = max(currTree[node * 2], currTree[node * 2 + 1]);
}
int query_tree(vector <int> &currTree, int node, int st, int dr, int left, int right)
{
int maxim = 0;
if(left <= st && dr <= right)
return currTree[node];
int med = (st + dr) / 2;
if(left <= med)
maxim = max(maxim, query_tree(currTree, node * 2, st, med, left, right));
else
maxim = max(maxim, query_tree(currTree, node * 2 + 1, med + 1, dr, left, right));
return maxim;
}
int query(int x, int y)
{
int maxim = 0;
while(cul[x] != cul[y]) {
if(nivel[f[lant[cul[x]].back()]] > nivel[f[lant[cul[y]].back()]]) {
maxim = max(maxim, query_tree(tree[cul[x]], 1, 1, lant[cul[x]].size(), order[x], order[lant[cul[x]].back()]));
x = f[lant[cul[x]].back()];
} else {
maxim = max(maxim, query_tree(tree[cul[y]], 1, 1, lant[cul[y]].size(), order[y], order[lant[cul[y]].back()]));
y = f[lant[cul[y]].back()];
}
}
maxim = max(maxim, query_tree(tree[cul[x]], 1, 1, lant[cul[x]].size(), min(order[x], order[y]), max(order[x], order[y])));
return maxim;
}
int main() {
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; ++i) {
cin >> cost[i];
}
for(int i = 1; i <= n - 1; ++i) {
int x, y;
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
dfs(1, -1, 1);
tree.resize(lant.size());
for(int i = 0; i < lant.size(); ++i) {
assert(lant[i].size() > 0);
tree[i].resize(lant[i].size() * 4);
for(int j = 0; j < lant[i].size(); ++j)
order[lant[i][j]] = j + 1;
}
for(int i = 1; i <= n; ++i)
update_tree(tree[cul[i]], 1, 1, lant[cul[i]].size(), order[i], cost[i]);
for(int i = 1; i <= m; ++i) {
int task;
cin >> task;
if(task == 0) {
int node, val;
cin >> node >> val;
update_tree(tree[cul[node]], 1, 1, lant[cul[node]].size(), order[node], val);
} else {
int x, y;
cin >> x >> y;
cout << query(x, y) << "\n";
}
}
return 0;
}