#include <iostream>
#include <fstream>
#include <cstdlib>
#include <climits>
#include <iomanip>
#include <set>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <iterator>
#include <vector>
#include <algorithm>
#include <numeric>
#include <queue>
#include <stack>
#include <bitset>
#include <functional>
#include <utility>
#include <cmath>
#include <string>
#include <deque>
#include <array>
#include <tuple>
#include <cstring>
#include <ctime>
#include <random>
#include <chrono>
///#include <windows.h>
#pragma GCC optimize("fast-math")
#define ll int
#define ld long double
#define pll pair<ll, ll>
#define psi pair<short int, short int>
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
#define all(x) (x).begin() + 1, (x).end()
#define all0(x) (x).begin(), (x).end()
#define allrev(x) (x).rbegin(), (x).rend() - 1
#define allrev0(x) (x).rbegin(), (x).rend()
#define println(thing) cout << thing << '\n'
#define yes println("YES")
#define no println("NO")
#define maybe println("MAYBE")
#define mda println("/////////////////")
#define msb(x) ((x) == 0 ? 0 : (1 << (64 - __builtin_clzll(x) - 1)))
#define lsb(x) ((x) & (-x))
using namespace std;
mt19937_64 rng(chrono :: steady_clock :: now().time_since_epoch().count());
const size_t fixed_random = rng();
clock_t start_time = clock(), end_time;
ll random_seed(ll modulo) {
return rng() % modulo;
}
template <typename T>
inline void hash_combine(size_t& seed, const T& value) {
seed ^= hash<T>{}(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
struct pair_hash {
template <typename T1, typename T2>
size_t operator () (const pair<T1, T2>& p) const {
size_t seed = fixed_random;
hash_combine(seed, p.first);
hash_combine(seed, p.second);
return seed;
}
};
struct normal_hash {
template <typename T>
size_t operator () (const T& p) const {
size_t seed = fixed_random;
hash_combine(seed, p);
return seed;
};
};
template <typename unordered_Map>
void optimize_map(unordered_Map& M) {
M.reserve(1024);
M.max_load_factor(0.5);
}
template <typename T1, typename T2>
istream& operator >> (istream& in, pair<T1, T2>& p) {
in >> p.first >> p.second;
return in;
}
template <typename T1, typename T2>
ostream& operator << (ostream& out, pair<T1, T2>& p) {
out << p.first << ' ' << p.second;
return out;
}
/// for bitset bits
template <typename T1, typename T2> T1 operator ^= (T1 t1, T2 t2) {t1 = t1 ^ t2; return t1;}
template <typename T1, typename T2> T1 operator |= (T1 t1, T2 t2) {t1 = t1 | t2; return t1;}
template <typename T1, typename T2> T1 operator &= (T1 t1, T2 t2) {t1 = t1 & t2; return t1;}
const ll NMX = (ll) 1e5 + 4e6 + 1;
const ll MOD = (ll) 1e9 + 7;
const ll INF = (ll) 1e17;
ll binExp(ll base, ll exp) {
ll P = 1;
for (ll bit = 1; bit <= exp; bit <<= 1) {
if (exp & bit)
P = (P * base) % MOD;
base = (base * base) % MOD;
}
return P;
}
inline ll modInv(ll x) {
return binExp(x, MOD - 2);
}
ll test_case = 0;
class maxSegTree {
private:
vector<ll> tree;
ll len;
public:
maxSegTree(ll _len) : len(_len), tree(4 * _len + 1, 0) {}
void build(vector<ll> &arr, ll tl, ll tr, ll v) {
if (tl == tr) {
tree[v] = arr[tl];
return;
}
ll tm = ((tl + tr) >> 1);
build(arr, tl, tm, 2 * v);
build(arr, tm + 1, tr, 2 * v + 1);
tree[v] = max(tree[2 * v], tree[2 * v + 1]);
}
void query(ll l, ll r, ll tl, ll tr, ll v, ll &mx) {
if (l <= tl && tr <= r) {
mx = max(mx, tree[v]);
return;
}
ll tm = ((tl + tr) >> 1);
if (l <= tm) {
query(l, r, tl, tm, 2 * v, mx);
}
if (r > tm) {
query(l, r, tm + 1, tr, 2 * v + 1, mx);
}
return;
}
void update(ll pos, ll val, ll tl, ll tr, ll v) {
if (tl == tr) {
tree[v] = val;
return;
}
ll tm = ((tl + tr) >> 1);
if (pos <= tm) {
update(pos, val, tl, tm, 2 * v);
}
if (pos > tm) {
update(pos, val, tm + 1, tr, 2 * v + 1);
}
tree[v] = max(tree[2 * v], tree[2 * v + 1]);
return;
}
};
void solve(ll testcase)
{
ll n, q;
cin >> n >> q;
vector<ll> a(n + 1);
for (ll i = 1; i <= n; i++) {
cin >> a[i];
}
vector<vector<ll>> g(n + 1);
for (ll i = 1, v, u; i <= n - 1; i++) {
cin >> v >> u;
g[v].push_back(u);
g[u].push_back(v);
}
vector<ll> pv(n + 1);
function<void(ll, ll)> find_parents = [&](ll v, ll par) {
for (ll u : g[v]) {
if (u != par) {
pv[u] = v;
find_parents(u, v);
}
}
return;
};
find_parents(1, -1);
const ll LOG = log2(n);
vector<vector<ll>> jump(n + 1, vector<ll>(LOG + 1));
for (ll i = 1; i <= n; i++) {
jump[i][0] = pv[i];
}
for (ll j = 1; j <= LOG; j++) {
for (ll i = 1; i <= n; i++) {
jump[i][j] = jump[jump[i][j - 1]][j - 1];
}
}
vector<ll> time_in(n + 1);
vector<ll> time_out(n + 1);
time_out[0] = INT_MAX;
ll time = 0;
function<void(ll, ll)> find_time = [&](ll v, ll par) {
time_in[v] = ++time;
for (ll u : g[v]) {
if (u != par) {
find_time(u, v);
}
}
time_out[v] = ++time;
};
function<bool(ll, ll)> is_ancestor = [&](ll v, ll u) {
return time_in[u] <= time_in[v] && time_out[u] >= time_out[v];
};
function<ll(ll, ll)> LCA = [&](ll v, ll u) {
if (is_ancestor(v, u)) {
return u;
}
if (is_ancestor(u, v)) {
return v;
}
for (ll j = LOG; j >= 0; j--) {
if (!is_ancestor(u, jump[v][j])) {
v = jump[v][j];
}
}
return jump[v][0];
};
find_time(1, -1);
vector<ll> subtree_size(n + 1);
function<ll(ll, ll)> find_subtree_sizes = [&](ll v, ll par) {
subtree_size[v] = 1;
for (ll u : g[v]) {
if (u != par) {
subtree_size[v] += find_subtree_sizes(u, v);
}
}
return subtree_size[v];
};
find_subtree_sizes(1, -1);
vector<ll> heavy_son(n + 1);
function<void(ll, ll)> find_heavy_sons = [&](ll v, ll par) {
ll heaviest_subtree = 0;
for (ll u : g[v]) {
if (u != par) {
heaviest_subtree = max(heaviest_subtree, subtree_size[u]);
find_heavy_sons(u, v);
}
}
if (!heaviest_subtree) {
return;
}
for (ll u : g[v]) {
if (u != par) {
if (subtree_size[u] == heaviest_subtree) {
heavy_son[v] = u;
break;
}
}
}
};
find_heavy_sons(1, -1);
vector<ll> label(n + 1);
ll label_counter = 0;
function<void(ll, ll)> label_dfs = [&](ll v, ll par) {
label[v] = ++label_counter;
if (heavy_son[v]) {
label_dfs(heavy_son[v], v);
}
for (ll u : g[v]) {
if (u != par && u != heavy_son[v]) {
label_dfs(u, v);
}
}
};
label_dfs(1, -1);
vector<ll> top_chain(n + 1);
function<void(ll, ll)> find_top_chains = [&](ll v, ll par) {
if (heavy_son[par] == v) {
top_chain[v] = top_chain[par];
}
else if (heavy_son[v]) {
top_chain[v] = v;
}
for (ll u : g[v]) {
if (u != par) {
find_top_chains(u, v);
}
}
return;
};
find_top_chains(1, -1);
maxSegTree heavy_chains(n);
vector<ll> to_build(n + 1);
function<void(ll, ll)> build_heavy_chains = [&](ll v, ll par) {
if (!heavy_son[v] && heavy_son[par] == v) {
ll top = label[top_chain[v]];
ll bottom = label[v];
ll vertex = top_chain[v];
while (heavy_son[vertex]) {
to_build[label[vertex]] = a[vertex];
vertex = heavy_son[vertex];
}
to_build[label[vertex]] = a[vertex];
}
for (ll u : g[v]) {
if (u != par) {
build_heavy_chains(u, v);
}
}
return;
};
build_heavy_chains(1, -1);
heavy_chains.build(to_build, 1, n, 1);
for (ll qq = 1, op, x, y; qq <= q; qq++) {
cin >> op >> x >> y;
if (op == 0) { /// update a[x] to y
if (heavy_son[x] || heavy_son[pv[x]] == x) {
heavy_chains.update(label[x], y, 1, n, 1);
}
else {
a[x] = y;
}
}
else if (op == 1) { /// query from x to LCA(x, y) and from y to LCA(x, y)
ll mx = INT_MIN, ancestor = LCA(x, y);
while (x != pv[ancestor]) {
if (heavy_son[x] || heavy_son[pv[x]] == x) {
if (is_ancestor(top_chain[x], ancestor)) {
ll top = label[top_chain[x]];
ll bottom = label[x];
heavy_chains.query(top, bottom, 1, n, 1, mx);
x = pv[top_chain[x]];
}
else {
ll top = label[ancestor];
ll bottom = label[x];
heavy_chains.query(top, bottom, 1, n, 1, mx);
x = pv[ancestor];
}
}
else {
mx = max(mx, a[x]);
x = pv[x];
}
}
while (y != pv[ancestor]) {
if (heavy_son[y] || heavy_son[pv[y]] == y) {
if (is_ancestor(top_chain[y], ancestor)) {
ll top = label[top_chain[y]];
ll bottom = label[y];
heavy_chains.query(top, bottom, 1, n, 1, mx);
y = pv[top_chain[y]];
}
else {
ll top = label[ancestor];
ll bottom = label[y];
heavy_chains.query(top, bottom, 1, n, 1, mx);
y = pv[ancestor];
}
}
else {
mx = max(mx, a[y]);
y = pv[y];
}
}
cout << mx << '\n';
}
}
return;
}
int main(void)
{
freopen("heavypath.in", "r", stdin);
freopen("heavypath.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(nullptr);
///cout << fixed << setprecision(20);
///ll tt; cin >> tt; while (tt--)
solve(++test_case);
\
return 0;
}
/**
///
**/