#include <fstream>
#include <vector>
using namespace std;
ifstream f("heavypath.in");
ofstream g("heavypath.out");
const int NMAX = 1e5 + 1;
const int ROOT = 1;
const int INF = 1e9;
int N, Q, v[NMAX];
vector < int > G[NMAX];
int w[NMAX], Max_S[NMAX], level[NMAX], father[NMAX];
int CNT = 1;
int Id[NMAX], pos[NMAX], First[NMAX], Size[NMAX];
static inline int my_min (int a, int b)
{
return ((a < b) ? a : b);
}
static inline int my_max (int a, int b)
{
return ((a > b) ? a : b);
}
class SegmentTree
{
int *A;
public:
inline void Construct (int Size)
{
A = new int [((Size << 2) + 1)];
for(int i = 1; i <= (Size << 2); ++i)
A[i] = -INF;
return;
}
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((Node << 1), a, mid, pos, Val);
else
Update(((Node << 1) + 1), mid + 1, b, pos, Val);
A[Node] = my_max(A[(Node << 1)], A[((Node << 1) + 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((Node << 1), a, mid, qa, qb);
if(qb > mid)
p_Right = Query(((Node << 1) + 1), mid + 1, b, qa, qb);
return my_max(p_Left, p_Right);
}
} AINT[NMAX];
static inline void Add_Edge (int u, int v)
{
G[u].push_back(v), G[v].push_back(u);
return;
}
static inline void Read ()
{
f.tie(nullptr);
f >> N >> Q;
for(int i = 1; i <= N; ++i)
f >> v[i];
for(int i = 1; i < N; ++i)
{
int u = 0, v = 0;
f >> u >> v;
Add_Edge(u, v);
}
return;
}
static inline void Find (int Node, int from = 0, int lvl = 0)
{
w[Node] = 1, father[Node] = from, level[Node] = lvl, Max_S[Node] = -1;
for(auto it : G[Node])
if(it != from)
{
Find(it, Node, lvl + 1);
w[Node] += w[it];
if(Max_S[Node] == -1 || (Max_S[Node] != -1 && w[it] > w[Max_S[Node]]))
Max_S[Node] = it;
}
return;
}
static inline void DFS (int Node, int from = 0)
{
Id[Node] = CNT;
pos[Node] = ++Size[CNT];
if(Size[CNT] == 1)
First[CNT] = Node;
if(Max_S[Node] != -1)
DFS(Max_S[Node], Node);
else
return;
for(auto it : G[Node])
if(it != from && it != Max_S[Node])
++CNT, DFS(it, Node);
return;
}
static inline void HLD ()
{
Find(ROOT);
DFS(ROOT);
for(int i = 1; i <= CNT; ++i)
AINT[i].Construct(Size[i]);
for(int i = 1; i <= N; ++i)
AINT[Id[i]].Update(1, 1, Size[Id[i]], pos[i], v[i]);
return;
}
static inline void my_swap (int &x, int &y)
{
x = (x ^ y), y = (x ^ y), x = (x ^ y);
return;
}
static inline int Query (int x, int y)
{
if(Id[x] == Id[y])
return AINT[Id[x]].Query(1, 1, Size[Id[x]], my_min(pos[x], pos[y]), my_max(pos[x], pos[y]));
if(level[First[Id[x]]] > level[First[Id[y]]])
my_swap(x, y);
return my_max(Query(y, First[Id[y]]), Query(father[First[Id[y]]], x));
}
static inline void TestCase ()
{
bool _type;
int x, y;
f >> _type >> x >> y;
if(_type == 0)
{
AINT[Id[x]].Update(1, 1, Size[Id[x]], pos[x], y);
return;
}
g << Query(x, y) << '\n';
return;
}
static inline void Solve ()
{
while(Q--)
TestCase();
return;
}
int main()
{
Read();
HLD();
Solve();
return 0;
}