#include <fstream>
#include <vector>
#include <algorithm>
#define REP(i,n) for(int i = 0;i < (int)n;i++)
using namespace std;
ifstream cin("heavypath.in");
ofstream cout("heavypath.out");
const int nmax = int(1e5) + 2;
int n, m;
int value[nmax];
int level[nmax];
int weight[nmax];
int nodeIndex[nmax];
int pathParent[nmax];
int nodePath[nmax];
int pathNumber;
vector< vector<int> > paths, segmentTrees;
vector<int> G[nmax];
inline void readData() {
cin>>n>>m;
for(int i = 1;i <= n;i++) {
cin>>value[i];
}
int a, b;
for(int i = 1;i < n;i++) {
cin>>a>>b;
G[a].push_back(b);
G[b].push_back(a);
}
}
void df(int v,int vp) {
weight[v] = 1;
int heavyestSon = -1;
for(auto w : G[v]) {
if(w == vp) continue;
level[w] = level[v] + 1;
df(w,v);
weight[v] += weight[w];
if(heavyestSon == -1 || weight[heavyestSon] < weight[w]) {
heavyestSon = w;
}
}
if(heavyestSon == -1) {
paths.push_back(vector<int>());
paths.back().push_back(v);
nodePath[v] = pathNumber++;
} else {
nodePath[v] = nodePath[heavyestSon];
paths[nodePath[v]].push_back(v);
for(auto w : G[v]) {
if(w == vp || w == heavyestSon) continue;
pathParent[nodePath[w]] = v;
}
}
}
void buildTree(const int &treeIndex,int node,int l,int r) {
if(l > r) return;
if(l == r) {
segmentTrees[treeIndex][node] = value[paths[treeIndex][l - 1]];
return;
}
int mid = (l + r)>>1;
buildTree(treeIndex,node<<1,l,mid);
buildTree(treeIndex,node<<1 | 1,mid + 1,r);
segmentTrees[treeIndex][node] = max(segmentTrees[treeIndex][node<<1],segmentTrees[treeIndex][node<<1 | 1]);
}
inline void buildTrees() {
REP(i,pathNumber) {
reverse(paths[i].begin(),paths[i].end());
REP(j,paths[i].size()) {
nodeIndex[paths[i][j]] = j + 1;
}
segmentTrees.push_back(vector<int>(paths[i].size()<<2,0));
buildTree(i,1,1,paths[i].size());
}
}
void updateTree(const int &treeIndex,int node,int l,int r,int pos) {
if(l == r) {
segmentTrees[treeIndex][node] = value[paths[treeIndex][l - 1]];
return;
}
int mid = (l + r)>>1;
pos <= mid ? updateTree(treeIndex,node<<1,l,mid,pos) : updateTree(treeIndex,node<<1 | 1,mid + 1,r,pos);
segmentTrees[treeIndex][node] = max(segmentTrees[treeIndex][node<<1],segmentTrees[treeIndex][node<<1 | 1]);
}
inline void update(const int &v,const int &newValue) {
value[v] = newValue;
updateTree(nodePath[v],1,1,paths[nodePath[v]].size(),nodeIndex[v]);
}
int queryTree(const int &treeIndex,int node,int l,int r,const int &ql,const int &qr) {
if(r < ql || l > qr ) return 0;
if(ql <= l && r <= qr) {
return segmentTrees[treeIndex][node];
}
int mid = (l + r)>>1;
return max(queryTree(treeIndex,node<<1,l,mid,ql,qr),queryTree(treeIndex,node<<1 | 1,mid + 1,r,ql,qr));
}
int query(int a,int b) {
if(a == b) {
return value[a];
}
if(nodePath[a] == nodePath[b]) {
int posA = nodeIndex[a];
int posB = nodeIndex[b];
if(posA > posB) swap(posA,posB);
return queryTree(nodePath[a],1,1,paths[nodePath[a]].size(),posA,posB);
}
if(level[pathParent[nodePath[a]]] > level[pathParent[nodePath[b]]]) {
return max(query(pathParent[nodePath[a]],b),queryTree(nodePath[a],1,1,paths[nodePath[a]].size(),1,nodeIndex[a]));
}
return max(query(pathParent[nodePath[b]],a),queryTree(nodePath[b],1,1,paths[nodePath[b]].size(),1,nodeIndex[b]));
}
int main()
{
readData();
level[1] = 1;
df(1,1);
buildTrees();
for(int t, a, b;m;m--) {
cin>>t>>a>>b;
if(!t) {
update(a,b);
} else {
cout<<query(a,b)<<"\n";
}
}
return 0;
}