#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int NMAX = 1e5 + 10, ROOT = 1;
int N, Q, A[NMAX];
vector < int > G[NMAX], Sons[NMAX];
bool Sel[NMAX];
int Level[NMAX], father[NMAX], W[NMAX], Max_Son[NMAX];
int M = 1;
int Chain[NMAX], Pos[NMAX], First[NMAX], Size[NMAX];
int sp[NMAX];
class SegmentTree
{
static const int NMAX = 1e5 + 10;
int A[(NMAX << 2)];
public:
inline void Update (int Node, int a, int b, int pos, int val)
{
if(a == b)
{
A[Node] = val;
return;
}
int Mid = (a + b) >> 1;
if(pos <= Mid)
Update(2 * Node, a, Mid, pos, val);
if(pos > Mid)
Update(2 * Node + 1, Mid + 1, b, pos, val);
A[Node] = max(A[2 * Node], A[2 * Node + 1]);
return;
}
inline int Query (int Node, int a, int b, int qa, int qb)
{
if(qa <= a && b <= qb)
return A[Node];
int Mid = (a + b) >> 1;
int p_Left = 0, p_Right = 0;
if(qa <= Mid)
p_Left = Query(2 * Node, a, Mid, qa, qb);
if(qb > Mid)
p_Right = Query(2 * Node + 1, Mid + 1, b, qa, qb);
return max(p_Left, p_Right);
}
} AINT;
static inline void Read ()
{
ios_base :: sync_with_stdio(false);
cin.tie(nullptr);
freopen("heavypath.in", "r", stdin);
freopen("heavypath.out", "w", stdout);
scanf("%d%d", &N, &Q);
for(int i = 1; i <= N; ++i)
scanf("%d", &A[i]);
for(int i = 1; i < N; ++i)
{
int X = 0, Y = 0;
scanf("%d%d", &X, &Y);
G[X].push_back(Y);
G[Y].push_back(X);
}
return;
}
static inline void Go (int Node, int From, int Lvl)
{
Level[Node] = Lvl;
father[Node] = From;
W[Node] = 1;
Max_Son[Node] = -1;
Sel[Node] = 1;
for(auto it : G[Node])
if(!Sel[it])
{
Go(it, Node, Lvl + 1);
Sons[Node].push_back(it);
W[Node] += W[it];
if(Max_Son[Node] == -1 || (Max_Son[Node] > 0 && W[it] > W[Max_Son[Node]]))
Max_Son[Node] = it;
}
return;
}
static inline void DFS (int Node)
{
Chain[Node] = M;
Pos[Node] = (++Size[M]);
if(Pos[Node] == 1)
First[M] = Node;
if(Max_Son[Node] == -1)
return;
DFS(Max_Son[Node]);
for(auto it : Sons[Node])
if(it == Max_Son[Node])
continue;
else
{
++M;
DFS(it);
}
return;
}
static inline void Update (int Id, int pos, int val)
{
AINT.Update(1, 1, N, sp[Id - 1] + pos, val);
return;
}
static inline int Query (int Id, int pos1, int pos2)
{
return AINT.Query(1, 1, N, sp[Id - 1] + pos1, sp[Id - 1] + pos2);
}
static inline void Load ()
{
for(int i = 1; i <= N; ++i)
Update(Chain[i], Pos[i], A[i]);
return;
}
static inline void Precalculation ()
{
Go(ROOT, 0, 1);
DFS(ROOT);
for(int i = 1; i <= M; ++i)
sp[i] = sp[i - 1] + Size[i];
Load();
return;
}
static inline int F (int X, int Y)
{
if(Chain[X] == Chain[Y])
return Query(Chain[X], min(Pos[X], Pos[Y]), max(Pos[X], Pos[Y]));
if(Level[First[Chain[X]]] > Level[First[Chain[Y]]])
swap(X, Y);
return max(F(Y, First[Chain[Y]]), F(father[First[Chain[Y]]], X));
}
static inline void TestCase ()
{
int Type = 0;
scanf("%d", &Type);
if(Type == 0)
{
int Node = 0, Val = 0;
scanf("%d%d", &Node, &Val);
Update(Chain[Node], Pos[Node], Val);
return;
}
int X = 0, Y = 0;
scanf("%d%d", &X, &Y);
printf("%d\n", F(X, Y));
return;
}
static inline void Solve ()
{
while(Q--)
TestCase();
return;
}
int main()
{
Read();
Precalculation();
Solve();
return 0;
}