#include <vector>
#include <functional>
#include <fstream>
#include <iostream>
#include <utility>
using namespace std;
template <typename Cmp>
class max_aint{
int n, minus_inf;
vector<int> buf;
Cmp c;
public:
template <typename F>
max_aint(const int sz, F f, Cmp cc, int min_inf): n(sz), buf(2*sz), c(cc), minus_inf(min_inf){
for(int i = 0; i < n; ++i){
buf[i+n] = f(i); }
for(int i = n-1; i > 0; --i){
buf[i] = max(buf[2*i], buf[2*i+1], c); } }
int query(int st, int dr)const{
int rez = minus_inf;
for(st += n, dr += n+1; st < dr; st /= 2, dr /= 2){
if(st%2){
rez = max(rez, buf[st++], c); }
if(dr%2){
rez = max(buf[--dr], rez, c); } }
return rez; }
int update(int p, const int v){
for(p += n, buf[p] = v, p /= 2; p > 0; p /= 2){
buf[p] = max(buf[2*p], buf[2*p+1], c); } } };
struct nod_info{
vector<int> adiac;
int adanc = 0, size = 1, heavy_child = -1, size_lant = 1, ind_lant = -1, val_init = 0,
father = 0, st = 0, dr = 0; };
void recon_dfs(vector<nod_info>& arb, const int cur, const int prev, vector<int>& euler){
euler.push_back(cur);
arb[cur].st = euler.size()-1;
for(const auto next : arb[cur].adiac){
if(next != prev){
arb[next].adanc = arb[cur].adanc+1;
arb[next].father = cur;
recon_dfs(arb, next, cur, euler);
arb[cur].size += arb[next].size;
euler.push_back(cur); } }
euler.push_back(cur);
arb[cur].dr = euler.size()-1;
if(prev != -1 && (arb[prev].heavy_child == -1 ||
arb[arb[prev].heavy_child].size < arb[cur].size)){
arb[prev].heavy_child = cur; }
if(arb[cur].heavy_child != -1){
arb[cur].size_lant = arb[arb[cur].heavy_child].size_lant + 1; } }
void decide_lant_dfs(vector<nod_info>& arb, const int cur, const int prev, int& care_lant,
vector<vector<int> >& paths){
if(arb[cur].ind_lant == -1){
arb[cur].ind_lant = care_lant++;
paths.push_back({}); }
if(arb[cur].heavy_child != -1){
arb[arb[cur].heavy_child].ind_lant = arb[cur].ind_lant; }
paths[arb[cur].ind_lant].push_back(cur);
for(const auto next : arb[cur].adiac){
if(next != prev){
decide_lant_dfs(arb, next, cur, care_lant, paths); } } }
class heavy_path{
vector<int> path_adanc, path_radacina;
vector<max_aint<less<int> > > paths_aint;
const vector<nod_info>& arb_ref;
public:
heavy_path(vector<nod_info>& arb):
arb_ref(arb){
vector<vector<int> > paths;
int nr_lanturi = 0;
decide_lant_dfs(arb, 0, -1, nr_lanturi, paths);
path_adanc.resize(nr_lanturi), path_radacina.resize(nr_lanturi);
for(int i = 0; i < nr_lanturi; ++i){
path_adanc[i] = arb[paths[i].front()].adanc;
path_radacina[i] = paths[i].front();
paths_aint.emplace_back(paths[i].size(),
[&paths, &arb, i](const int x){
return arb[paths[i][x]].val_init; },
less<int>(), -1); } }
void update(const int nod, const int val){
const int wh = arb_ref[nod].ind_lant;
paths_aint[wh].update(arb_ref[nod].adanc - path_adanc[wh], val); }
int query(const int sus, int jos){
int rez = 0;
while(arb_ref[sus].ind_lant != arb_ref[jos].ind_lant){
rez = max(rez,
paths_aint[arb_ref[jos].ind_lant].query(0, arb_ref[jos].adanc - path_adanc[arb_ref[jos].ind_lant]));
jos = arb_ref[path_radacina[arb_ref[jos].ind_lant]].father; }
return max(rez,
paths_aint[arb_ref[sus].ind_lant].query(
arb_ref[sus].adanc - path_radacina[arb_ref[sus].ind_lant],
arb_ref[jos].adanc - path_radacina[arb_ref[jos].ind_lant])); } };
int main(){
ifstream f("heavypath.in");
ofstream g("heavypath.out");
int n, m;
f >> n >> m;
vector<nod_info> arb(n);
for(int i = 0; i < n; ++i){
f >> arb[i].val_init; }
for(int i = 0, a, b; i < n-1; ++i){
f >> a >> b;
--a, --b;
arb[a].adiac.push_back(b);
arb[b].adiac.push_back(a); }
vector<int> euler;
recon_dfs(arb, 0, -1, euler);
auto cmp = [&arb](const int x, const int y){
return (x==-1) || ((y!=-1) && arb[x].adanc > arb[y].adanc); };
max_aint<decltype(cmp)> for_lca(euler.size(), [&euler](const int x){
return euler[x]; },
cmp, -1);
heavy_path hp(arb);
for(int i = 0, t, a, b; i < m; ++i){
f >> t >> a >> b;
if(t == 0){
hp.update(a-1, b); }
else{
--a, --b;
int x = arb[a].st, y = arb[b].st;
if(x > y){
swap(x, y); }
const int lca = for_lca.query(x, y);
int p, q;
g << max(p=hp.query(lca, a), q=hp.query(lca, b)) << '\n'; } }
return 0; }