#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <utility>
#include <algorithm>
#define BUF_SIZE 1<<17
char buffer[BUF_SIZE];
int pbuf = BUF_SIZE;
FILE *fi, *fo;
inline char nextch(){
if(pbuf == BUF_SIZE){
fread(buffer, BUF_SIZE, 1, fi);
pbuf=0;
}
return buffer[pbuf++];
}
inline long long nextnum(){
long long a = 0;
char c = nextch();
while((c < '0' || c > '9') && c != '-')
c = nextch();
int semn = 1;
if(c == '-'){
semn = -1;
c = nextch();
}
while('0' <= c && c <= '9'){
a = a*10+c-'0';
c = nextch();
}
return a*semn;
}
inline int maxim(int a, int b){
return a > b ? a : b;
}
#define MAXN 100000
#define MAXM 100000
int n, m;
struct query{
int type;
int val1;
int val2;
};
query operation[1 + MAXM];
int subTreeDim[1 + MAXN];
int level[1 + MAXN];
int used[1 + MAXN];
int v[1 + MAXN];
std::vector < int > G[1 + MAXN];
int aint[4 * MAXN];
int numPaths = 0;
int p[1+MAXN];
int pathDim[1+MAXN];
int pathFather[1+MAXN];
int pathLevel[1+MAXN];
int pathPosition[1+MAXN];
std::vector < int > path[1+MAXN];
void dfs (int node){
used[node] = 1;
subTreeDim[node] = 1;
int isLeaf = 1;
int nodeSubMax=-1;
for(int i = 0; i <G[node].size(); i++){
int son = G[node][i];
if(!used[son]){
isLeaf = 0;
level[son] = level[node] + 1;
dfs(son);
if(nodeSubMax == -1)
nodeSubMax = son;
else if(subTreeDim[nodeSubMax] < subTreeDim[son])
nodeSubMax = son;}}
if(isLeaf){
p[node] = ++numPaths;
pathDim[numPaths] = 1;
path[numPaths].push_back(node);}
else{
p[node] = p[nodeSubMax];
pathDim[p[node]]++;
path[p[node]].push_back(node);
for(int i = 0; i < G[node].size(); i++){
if(i != nodeSubMax && level[G[node][i]] >= level[node]){
pathFather[p[G[node][i]]] = node;
pathLevel[p[G[node][i]]] = level[node];
}}}
}
inline void buildAint(int node, int left, int right, int delay, int currPath){
if(left == right)
aint[node + delay] = v[path[currPath][left - 1]];
else{
int m = (left + right)/2;
buildAint(2 * node, left, m, delay, currPath);
buildAint(2 * node+1, m+1, right, delay, currPath);
aint[node + delay] = maxim(aint[2 * node + delay], aint[2 * node + 1 + delay]);}
}
inline void createPath(){
level[1]=1;
dfs(1);
for(int i = 1; i <= numPaths; i++){
reverse(path[i].begin(), path[i].end());
if(i > 1)
pathPosition[i] = pathPosition[i-1] + pathDim[i-1] * 4;
buildAint(1, 1, pathDim[i], pathPosition[i], i);}
}
void update(int node, int left, int right, int poz, int val, int delay){
if(left == right)
aint[node + delay] = val;
else{
int m = (left + right)/2;
if(poz <= m) update(2 * node, left, m, poz, val, delay);
else update(2 * node + 1, m + 1, right, poz, val, delay);
aint[node + delay] = maxim(aint[2 * node +delay], aint[2 * node + 1 +delay]);}
}
int query(int node, int left, int right, int qleft, int qright, int delay){
if(qleft <= left && right <= qright)
return aint[node + delay];
else{
int m = (left + right)/2;
int rez = 0;
if(qleft <= m) rez = maxim(rez, query(2 * node, left, m, qleft, qright, delay));
if(m < qright) rez = maxim(rez, query(2 * node + 1, m + 1, right, qleft, qright, delay));
return rez;}
}
int main(){
fi = fopen("heavypath.in","r");
fo = fopen("heavypath.out","w");
n = nextnum();
m = nextnum();
for(int i = 1; i <=n; i++)
v[i] = nextnum();
for(int i = 1; i < n; i++){
int a = nextnum(), b = nextnum();
G[a].push_back(b);
G[b].push_back(a);}
for(int i = 1; i <= m; i++){
operation[i].type = nextnum();
operation[i].val1 = nextnum();
operation[i].val2 = nextnum();}
createPath();
pathLevel[1] = 0;
for(int i = 1; i <=m; i++){
int x = operation[i].val1, y = operation[i].val2;
if(operation[i].type == 0)
update(1, 1, pathDim[p[x]], level[x] - pathLevel[p[x]], y, pathPosition[p[x]]);
else{
int sol = 0;
int flag = 1;
while(flag){
if(p[x] == p[y]){
if(level[x] > level[y])
std::swap(x, y);
sol = maxim(sol, query(1, 1, pathDim[p[x]], level[x] - pathLevel[p[x]], level[y] - pathLevel[p[x]], pathPosition[p[x]]));
flag = 0;}
else{
if(pathLevel[p[x]] < pathLevel[p[y]])
std::swap(x, y);
sol = maxim(sol, query(1, 1, pathDim[p[x]], 1, level[x] - pathLevel[p[x]], pathPosition[p[x]]));
x=pathFather[p[x]];}}
fprintf(fo,"%d\n", sol);}
}
fclose(fi);
fclose(fo);
return 0;
}