#include <fstream>
#include <algorithm>
#include <vector>
using namespace std;
ifstream fin("heavypath.in");
ofstream fout("heavypath.out");
const int MAX_N = 100000;
inline int max(int x, int y)
{
return (x > y) ? x : y;
}
inline void swap(int& x, int& y)
{
x ^= y ^= x ^= y;
}
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[MAX_N << 2 | 1];
void Build(int node, int left, int right, int pathIdx, int delta)
{
if(left == right)
{
tree[node + delta] = val[path[pathIdx][left - 1]];
return;
}
int mid = left + ((right - left) >> 1);
Build(node << 1, left, mid, pathIdx, delta);
Build(node << 1 | 1, mid + 1, right, pathIdx, delta);
tree[node + delta] = max(tree[(node << 1) + delta],
tree[(node << 1 | 1) + delta]);
}
void Update(int node, int left, int right, int pos, int val, int delta)
{
if(left == right)
{
tree[node + delta] = val;
return;
}
int mid = left + ((right - left) >> 1);
if(pos <= mid)
Update(node << 1, left, mid, pos, val, delta);
if(pos > mid)
Update(node << 1 | 1, mid + 1, right, pos, val, delta);
tree[node + delta] = max(tree[(node << 1) + delta],
tree[(node << 1 | 1) + delta]);
}
int Query(int node, int left, int right, int leftQuery, int rightQuery, int delta)
{
if(left > rightQuery || right < leftQuery)
return 0;
if(leftQuery <= left && right <= rightQuery)
return tree[node + delta];
int mid = left + ((right - left) >> 1);
return max(Query(node << 1, left, mid, leftQuery, rightQuery, delta),
Query(node << 1 | 1, mid + 1, right, leftQuery, rightQuery, delta));
}
};
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);
segTree.Build(1, 1, (int)path[i].size(), 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(1, 1, (int)path[idx[x]].size(), pos[x], pos[y], delay[idx[x]]);
}
if(depth[parent[idx[x]]] < depth[parent[idx[y]]])
swap(x, y);
return max(segTree.Query(1, 1, (int)path[idx[x]].size(), 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(1, 1, (int)path[idx[x]].size(), 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;
}