#include <fstream>
#include <vector>
#include <algorithm>
#include <bitset>
using namespace std;
ifstream cin("heavypath.in");
ofstream cout("heavypath.out");
const int MAX_N = 1e5 + 7;
vector < int > val_of(MAX_N);
vector < vector < int > > gr(MAX_N);
vector < int > root_of(MAX_N);
vector < int > level_of(MAX_N);
vector < int > rank_of(MAX_N);
vector < int > chain_nr(MAX_N);
vector < int > poz_of(MAX_N);
vector < vector < int > > chain;
bitset < MAX_N > used;
class Segment_tree {
public:
Segment_tree(int n, int chain_nr) {
_n = n;
_chain_nr = chain_nr;
_arb.resize(n << 2);
_Init(1, n, 1);
}
void Update(int poz, int val) {
_Update(val, poz, 1, _n, 1);
}
int Query(int l, int r) {
return _Query(l, r, 1, _n, 1);
}
private:
int _n;
int _chain_nr;
vector < int > _arb;
int _Init(int st, int dr, int cur_node) {
if (st == dr) {
_arb[cur_node] = val_of[chain[_chain_nr][st]];
return _arb[cur_node];
}
int mid = (st + dr) >> 1;
_arb[cur_node] = max(_Init(st, mid, cur_node << 1),
_Init(mid + 1, dr, cur_node << 1 | 1));
return _arb[cur_node];
}
void _Update(int val, int poz, int st, int dr, int cur_node) {
if (st == dr) {
_arb[cur_node] = val;
return ;
}
int mid = (st + dr) >> 1;
int left = cur_node << 1;
int right = cur_node << 1 | 1;
if (poz <= mid) {
_Update(val, poz, st, mid, left);
}
else {
_Update(val, poz, mid + 1, dr, right);
}
_arb[cur_node] = max(_arb[left], _arb[right]);
}
int _Query(int l, int r, int st, int dr, int cur_node) {
if (l <= st and dr <= r) {
return _arb[cur_node];
}
int mid = (st + dr) >> 1;
int left = -1;
int right = -1;
if (l <= mid) {
left = _Query(l, r, st, mid, cur_node << 1);
}
if (mid + 1 <= r) {
right = _Query(l, r, mid + 1, dr, cur_node << 1 | 1);
}
return max(left, right);
}
};
int Dfs(int cur_node, int cur_level, int &cnt) {
used[cur_node] = true;
int best_rank = -1;
bool went_on = false;
level_of[cur_node] = cur_level;
rank_of[cur_node] = 1;
for (auto next_node : gr[cur_node]) {
if (used[next_node]) {
continue;
}
went_on = true;
root_of[next_node] = cur_node;
int next_rank = Dfs(next_node, cur_level + 1, cnt);
if (next_rank > best_rank) {
chain_nr[cur_node] = chain_nr[next_node];
best_rank = next_rank;
}
rank_of[cur_node] += next_rank;
}
if (not went_on) {
chain_nr[cur_node] = ++ cnt;
return 1;
}
return rank_of[cur_node];
}
void Init(int n, int &cnt) {
for (int i = 1; i <= n; i ++) {
cin >> val_of[i];
}
for (int i = 1; i < n; i ++) {
int x, y;
cin >> x >> y;
gr[x].push_back(y);
gr[y].push_back(x);
}
root_of[1] = 1;
rank_of[1] = Dfs(1, 1, cnt);
}
void Make_chains(int n, int cnt) {
for (int i = 1; i <= n; i ++) {
int ch_nr = chain_nr[i];
if (not chain[ch_nr].size()) {
chain[ch_nr].push_back(-27);
}
chain[ch_nr].push_back(i);
}
for (int i = 1; i <= cnt; i ++) {
sort(chain[i].begin() + 1, chain[i].end(), [] (const int a, const int b) -> bool {
return level_of[a] < level_of[b];
});
for (int j = 1; j < chain[i].size(); j ++) {
poz_of[chain[i][j]] = j;
}
}
}
int get_top_level(int x) {
return level_of[chain[x][1]];
}
int get_root(int x) {
return root_of[chain[x][1]];
}
int main(int argc, char const *argv[]) {
int n, m;
cin >> n >> m;
int cnt = 0;
Init(n, cnt);
chain.resize(cnt + 1);
Make_chains(n, cnt);
vector < Segment_tree* > Tree(cnt + 1);
for (int i = 1; i <= cnt; i ++) {
Tree[i] = new Segment_tree(chain[i].size() - 1, i);
}
for (int i = 1; i <= m; i ++) {
int t, x, y;
cin >> t >> x >> y;
int ch_nr;
if (not t) {
ch_nr = chain_nr[x];
int poz = poz_of[x];
Tree[ch_nr] -> Update(poz, y);
continue;
}
int sol = -1;
while (chain_nr[x] != chain_nr[y]) {
int a = get_top_level(chain_nr[x]);
int b = get_top_level(chain_nr[y]);
if (a < b) {
swap(x, y);
}
ch_nr = chain_nr[x];
sol = max(sol, Tree[ch_nr] -> Query(1, poz_of[x]));
x = get_root(ch_nr);
}
ch_nr = chain_nr[x];
int poz1 = poz_of[x];
int poz2 = poz_of[y];
if (poz1 > poz2) {
swap(poz1, poz2);
}
sol = max(sol, Tree[ch_nr] -> Query(poz1, poz2));
cout << sol << '\n';
}
}