#include <fstream>
#include <vector>
#pragma GCC optimize("O3")
#include <algorithm>
using namespace std;
ifstream in("heavypath.in");
ofstream out("heavypath.out");
const int nmax = 1e5, max32_t = (1 << 30);
int n, nrq, a[nmax + 2], typee, xx, yy;
vector <int> edges[nmax + 2];
struct segmenttree{
int tree[4 * nmax + 2];
void build(int node, int st, int dr){
if(st != dr){
int mij = (st + dr) >> 1;
build((node << 1), st, mij);
build((node << 1) | 1, mij + 1, dr);
}; tree[node] = -max32_t; return;
}
void update(int node, int st, int dr, int idx, int valuee){
if(st == dr){
tree[node] = valuee;
}else{
int mij = (st + dr) >> 1;
if(idx <= mij) update((node << 1), st, mij, idx, valuee);
if(mij < idx) update((node << 1) | 1, mij + 1, dr, idx, valuee);
tree[node] = max(tree[(node << 1)], tree[(node << 1) | 1]);
}; return;
}
int query(int node, int st, int dr, int leftt, int rightt){
if(leftt <= st && dr <= rightt){
return tree[node];
}else{
int mij = (st + dr) >> 1, qry = -max32_t;
if(leftt <= mij) qry = max(qry, query((node << 1), st, mij, leftt, rightt));
if(mij < rightt) qry = max(qry, query((node << 1) | 1, mij + 1, dr, leftt, rightt));
return qry;
}; return -max32_t;
}
int _query(int leftt, int rightt){
if(leftt > rightt){ swap(leftt, rightt); }
return query(1, 1, n, leftt, rightt);
}
} segtree;
struct heavylightdecomposition{
int subtreesz[nmax + 2], depth[nmax + 2], father[nmax + 2];
int headpath[nmax + 2], whatchain[nmax + 2];
int hldtag[nmax + 2]; vector <int> chainnodes[nmax + 2];
void dfsbuild(int node, int parent){
subtreesz[node] = 1; father[node] = parent;
depth[node] = depth[parent] + 1;
int heavykiddo = 0;
for(auto nxt : edges[node]){
if(nxt == parent){ continue; }
dfsbuild(nxt, node);
subtreesz[node] += subtreesz[nxt];
/// get the chain from the biggest subtree ///
if(subtreesz[heavykiddo] < subtreesz[nxt]){
heavykiddo = nxt;
}
}
/// create / append to a chain ///
if(!heavykiddo) whatchain[heavykiddo]++;
whatchain[node] = whatchain[heavykiddo];
headpath[whatchain[node]] = node;
chainnodes[whatchain[node]].push_back(node);
return;
}
/// helper function ///
inline int getheadpath(int node){
return headpath[whatchain[node]];
}
void build(){
dfsbuild(1, 0); segtree.build(1, 1, n);
/// O(n) additional memory instead of another dfs ///
for(int i = 1, tag = 0; i <= whatchain[0]; i++){
reverse(chainnodes[i].begin(), chainnodes[i].end()); /// necessary
for(auto &xx : chainnodes[i]){
hldtag[xx] = (++tag); segtree.update(1, 1, n, hldtag[xx], a[xx]);
}
}
// for(int i = 1; i <= n; i++){
// out<<i<<" -> "<<whatchain[i]<<" | "<<getheadpath(i)<<" - "<<hldtag[i]<<"\n";
// }
return;
}
/// answer queries and updates ///
void update(int xx, int yy){
a[xx] = yy; /// change a[xx] = yy
segtree.update(1, 1, n, hldtag[xx], a[xx]);
return;
}
int query(int xx, int yy){ /// get max on path from xx to yy
int maxonpath = -max32_t;
for(; getheadpath(xx) != getheadpath(yy); ){
if(depth[getheadpath(xx)] < depth[getheadpath(yy)]){ swap(xx, yy); }
maxonpath = max(maxonpath, segtree._query(hldtag[xx], hldtag[getheadpath(xx)]));
xx = father[getheadpath(xx)];
}
maxonpath = max(maxonpath, segtree._query(hldtag[xx], hldtag[yy]));
return maxonpath;
}
} hld;
int main(){
in>>n>>nrq;
for(int i = 1; i <= n; i++){
in>>a[i];
}
for(int i = 1; i <= n - 1; i++){
in>>xx>>yy;
edges[xx].push_back(yy);
edges[yy].push_back(xx);
}
hld.build();
for(int itq = 1; itq <= nrq; itq++){
in>>typee>>xx>>yy;
if(typee == 0){
hld.update(xx, yy);
}else{
out<<hld.query(xx, yy)<<"\n";
}
}
return 0;
}