#include <iostream>
#include <cmath>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <cstring>
#include <chrono>
#include <cassert>
#include <bitset>
#include <stack>
#include <queue>
#include <iomanip>
#include <random>
#include <string>
#include <complex>
#include <algorithm>
#include <numeric>
#include <immintrin.h>
#ifdef _MSC_VER
# include <intrin.h>
# define __builtin_popcount __popcnt
# define __builtin_popcountll __popcnt64
#endif
#pragma warning(disable : 4996)
//#pragma GCC optimize("Ofast")
#define x first
#define y second
#define ld long double
#define ll long long
#define ull unsigned long long
#define us unsigned short
#define lsb(x) ((x) & (-(x)))
#define pii pair <int, int>
#define pll pair <ll, ll>
using namespace std;
//const int MOD = (int)1e9 + 7;
mt19937_64 gen(chrono::duration_cast<chrono::nanoseconds>(chrono::steady_clock::now().time_since_epoch()).count());
uniform_int_distribution <int> rng;
const ld PI = acos(-1);
const int MOD = (int)1e9 + 7;
class SegTree {
public:
vector <int> tree;
void init(int n) {
tree.resize(4 * n + 5);
fill(tree.begin(), tree.end(), 0);
}
void update(int node, int st, int dr, int ind, int val) {
if (st == dr) {
tree[node] = val;
return;
}
int mid = (st + dr) >> 1;
if (ind <= mid)
update(2 * node, st, mid, ind, val);
else
update(2 * node + 1, mid + 1, dr, ind, val);
tree[node] = max(tree[2 * node], tree[2 * node + 1]);
}
int query(int node, int st, int dr, int l, int r) {
if (l <= st && dr <= r)
return tree[node];
int mid = (st + dr) >> 1;
int ans = 0;
if (l <= mid)
ans = max(ans, query(2 * node, st, mid, l, r));
if (mid < r)
ans = max(ans, query(2 * node + 1, mid + 1, dr, l, r));
return ans;
}
};
class HLD {
public:
vector <vector <int>> g;
vector <int> top, ind, t, heavy_son, sz;
SegTree tree;
int timer;
int N;
void init(int n) {
g.resize(n + 1);
top.resize(n + 1);
ind.resize(n + 1);
sz.resize(n + 1);
t.resize(n + 1);
heavy_son.resize(n + 1);
tree.init(n);
N = n;
timer = 0;
}
void init(int v[]) {
for (int i = 1; i <= N; i++) {
tree.update(1, 1, N, ind[i], v[i]);
}
}
void add_egde(int x, int y) {
g[x].push_back(y);
g[y].push_back(x);
}
void dfs_init(int node, int par = 0) {
sz[node] = 1;
t[node] = par;
top[node] = node;
int mx = 0;
for (auto& son : g[node]) {
if (son == par)
continue;
dfs_init(son, node);
sz[node] += sz[son];
if (sz[son] > mx)
mx = sz[son], heavy_son[node] = son;
}
}
void dfs_build(int node, int par = 0) {
top[node] = top[par];
ind[node] = ++timer;
if (heavy_son[node])
dfs_build(heavy_son[node], par);
for (auto& son : g[node]) {
if (son == t[node] || son == heavy_son[node])
continue;
dfs_build(son, son);
}
}
void update(int x, int y) {
tree.update(1, 1, N, ind[x], y);
}
int query(int x, int y) {
int ans = 0;
while (top[x] != top[y]) {
if (ind[x] < ind[y])
swap(x, y);
ans = max(ans, tree.query(1, 1, N, ind[top[x]], ind[x]));
x = t[top[x]];
}
if (ind[x] < ind[y])
swap(x, y);
return max(ans, tree.query(1, 1, N, ind[y], ind[x]));
}
} hld;
int v[100005];
void solve(int test = 0) {
int n, q;
cin >> n >> q;
hld.init(n);
for (int i = 1; i <= n; i++)
cin >> v[i];
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
hld.add_egde(x, y);
}
hld.dfs_init(1);
hld.dfs_build(1, 1);
hld.init(v);
for (; q; q--) {
int t, x, y;
cin >> t >> x >> y;
if (t == 0) {
hld.update(x, y);
}
else {
cout << hld.query(x, y) << "\n";
}
}
}
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
srand(time(0));
#ifdef LOCAL
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#else
freopen("heavypath.in", "r", stdin);
freopen("heavypath.out", "w", stdout);
#endif
int T = 1;
//cin >> T;
for (int t = 1; t <= T; t++) {
solve(t);
}
return 0;
}