#include <fstream>
#include <algorithm>
#include <vector>
using namespace std;
ifstream fin("heavypath.in");
ofstream fout("heavypath.out");
const int MAX_N = 100000,
TREE_SIZE = 262144;
inline int max(int x, int y)
{
return (x > y) ? x : y;
}
inline void swap(int& x, int& y)
{
x ^= y ^= x ^= y;
}
int NextPow2(int val)
{
--val;
val |= val >> 1;
val |= val >> 2;
val |= val >> 4;
val |= val >> 8;
val |= val >> 16;
return val + 1;
}
vector<int> adj[MAX_N + 1],
path[MAX_N + 1];
int val[MAX_N + 1],
depth[MAX_N + 1],
subSize[MAX_N + 1];
int parent[MAX_N + 1],
idx[MAX_N + 1],
pos[MAX_N + 1];
int n, q, pathsCount;
struct SegmentTree
{
int tree[TREE_SIZE];
void Build(int pathIdx, int delta)
{
int pathSize = (int)path[pathIdx].size(),
n = NextPow2(pathSize);
for(int i = 0; i < pathSize; i++)
tree[i + n + delta] = val[path[pathIdx][i]];
for(int i = n - 1; i > 0; i--)
tree[i + delta] = max(tree[(i << 1) + delta], tree[(i << 1 | 1) + delta]);
}
void Update(int pathIdx, int pos, int val, int delta)
{
int pathSize = (int)path[pathIdx].size(),
n = NextPow2(pathSize);
pos += n - 1;
tree[pos + delta] = val;
pos >>= 1;
while(pos > 0)
{
tree[pos + delta] = max(tree[(pos << 1) + delta], tree[(pos << 1 | 1) + delta]);
pos >>= 1;
}
}
int Query(int pathIdx, int left, int right, int delta)
{
int pathSize = (int)path[pathIdx].size(),
n = NextPow2(pathSize);
int res = 0;
left += n - 1;
right += n - 1;
while(left <= right)
{
if(left & 1)
{
res = max(res, tree[left + delta]);
left++;
}
if(!(right & 1))
{
res = max(res, tree[right + delta]);
right--;
}
left >>= 1;
right >>= 1;
}
return res;
}
};
SegmentTree segTree;
int delay[MAX_N + 1];
void Read()
{
fin >> n >> q;
for(int i = 1; i <= n; i++)
fin >> val[i];
for(int i = 1; i < n; i++)
{
int x, y;
fin >> x >> y;
adj[x].push_back(y);
adj[y].push_back(x);
}
}
void DFS(int node, int father)
{
bool leaf = true;
int heavySon = -1;
depth[node] = 1 + depth[father];
subSize[node] = 1;
for(int son : adj[node])
{
if(son == father)
continue;
DFS(son, node);
leaf = false;
subSize[node] += subSize[son];
if(heavySon == -1 || subSize[heavySon] < subSize[son])
heavySon = son;
}
if(leaf)
{
++pathsCount;
path[pathsCount].push_back(node);
idx[node] = pathsCount;
return;
}
path[idx[heavySon]].push_back(node);
idx[node] = idx[heavySon];
for(int son : adj[node])
{
if(son == father || son == heavySon)
continue;
parent[idx[son]] = node;
}
}
void MakePaths()
{
for(int i = 1; i <= pathsCount; i++)
{
reverse(path[i].begin(), path[i].end());
int ind = 0;
for(int node : path[i])
pos[node] = ++ind;
delay[i] = delay[i - 1] + ((int)path[i - 1].size() << 2);
//delay[i] = delay[i - 1] + (NextPow2((int)path[i - 1].size()) << 1);
segTree.Build(i, delay[i]);
}
}
int Query(int x, int y)
{
if(idx[x] == idx[y])
{
if(pos[x] > pos[y])
swap(x, y);
return segTree.Query(idx[x], pos[x], pos[y], delay[idx[x]]);
}
if(depth[parent[idx[x]]] < depth[parent[idx[y]]])
swap(x, y);
return max(segTree.Query(idx[x], 1, pos[x], delay[idx[x]]),
Query(parent[idx[x]], y));
}
void SolveQueries()
{
while(q--)
{
int t, x, y;
fin >> t >> x >> y;
if(t == 0)
segTree.Update(idx[x], pos[x], y, delay[idx[x]]);
else
if(t == 1)
fout << Query(x, y) << '\n';
}
}
int main()
{
Read();
DFS(1, 0);
MakePaths();
SolveQueries();
fin.close();
fout.close();
return 0;
}