#include <fstream>
#include <vector>
#include <algorithm>
#include <bitset>
#define maxn 100001
using namespace std;
ifstream fin("heavypath.in");
ofstream fout("heavpath.out");
int value[maxn],depth[maxn]; // indexed by individual nodes in the graph
int parent[maxn],which_path[maxn],segment_tree_pos[maxn],path_length[maxn]; // indexed by the decomposed paths which can be n-1 in the worst case
int segment_tree [4*maxn]; //is actually a conglomerate of segment tress (one for each decomposed path) fitted into one array, the "nx4" size makes sure there is enough space for everything
int x,y,n,m,total_paths;
bool op;
vector <int> G[maxn],P[maxn];
bitset <maxn> visited;
int DFS (int x)
{
visited[x]=1;
bool leaf=1;
int sum_nodes_below=0,child_nodes=0,heaviest_path=0,heaviest_child;
for (vector<int>::iterator it= G[x].begin(); it!=G[x].end(); ++it)
{
if (visited[*it]) continue;
leaf=0;
depth[*it]=depth[x]+1;
child_nodes = DFS (*it);
sum_nodes_below += child_nodes;
if (child_nodes > heaviest_path)
{
heaviest_path=child_nodes;
heaviest_child=*it;
}
}
if (leaf)
{
++total_paths;
P[total_paths].push_back (x);
path_length[total_paths]=1;
which_path [x]=total_paths;
return 1;
}
P[which_path[heaviest_child]].push_back (x);
path_length[which_path[heaviest_child]]++;
which_path[x] = which_path[heaviest_child];
for (vector<int>::iterator it=G[x].begin(); it!=G[x].end(); it++)
{
if (*it==heaviest_child || depth[*it]<depth[x]) continue;
parent[which_path[*it]]=x;
}
return sum_nodes_below+1;
}
void build_segment_tree (int node, int left, int right, int jump, int path)
{
if (left==right)
{
segment_tree[node+jump]=value[P[path][left-1]];
return;
}
int mid = (left+right)/2;
build_segment_tree (node*2,left,mid,jump,path);
build_segment_tree (node*2+1,mid+1,right,jump,path);
segment_tree[node+jump] = max (segment_tree[node*2 + jump],segment_tree[node*2 + 1 + jump]);
}
void make_paths ()
{
depth[1]=1;
DFS (1);
segment_tree_pos[1]=1;
for (int i=1; i<=total_paths; i++)
{
reverse (P[i].begin(),P[i].end());
if (i>1) segment_tree_pos[i] = segment_tree_pos[i-1] + 4*path_length[i-1];
build_segment_tree (1, 1, path_length[i],segment_tree_pos [i],i);
}
}
void update (int node, int left, int right, int target, int val, int jump)
{
if (left==right) {segment_tree[node+jump] = val; return;}
int mid = (left + right)/2;
if (target<=mid) update (node*2,left,mid,target,val,jump);
else update (node*2+1,mid+1,right,target,val,jump);
segment_tree[node+jump]=max (segment_tree[node*2+jump],segment_tree[node*2+1+jump]);
}
int query (int node, int left, int right, int left_target, int right_target, int jump)
{
if (left_target<=left && right_target>=right) return segment_tree[node+jump];
int mid = (left+right)/2,maxv=0;
if (left_target<=mid) maxv = max (maxv,query(node*2,left,mid,left_target,right_target,jump));
if (right_target>mid) maxv = max (maxv,query(node*2+1,mid+1,right,left_target,right_target,jump));
return maxv;
}
void operation (bool type, int x, int y)
{
int target,path,target1,target2;
if (!type)
{
target = depth[x] - depth[parent[which_path[x]]];
update (1,1,path_length[which_path[x]],target,y,segment_tree_pos[which_path[x]]);
}
else
{
int maxv=0;
while (which_path[x]!=which_path[y])
{
if (depth[parent[which_path[x]]] < depth[parent[which_path[y]]]) swap (x,y);
target = depth[x] - depth[parent[which_path[x]]];
maxv = max (maxv, query(1,1,path_length[which_path[x]],1,target,segment_tree_pos[which_path[x]]));
x=parent[which_path[x]];
}
target1 = depth[x] - depth[parent[which_path[x]]];
target2 = depth[y] - depth[parent[which_path[y]]];
if (target1>target2) swap (target1,target2);
maxv = max (maxv, query(1,1,path_length[which_path[x]],target1,target2,segment_tree_pos[which_path[x]]));
fout<<maxv<<"\n";
}
}
int main()
{
fin>>n>>m;
for (int i=1; i<=n; i++) fin>>value[i];
for (int i=1; i<n; i++)
{
fin>>x>>y;
G[x].push_back(y);
G[y].push_back(x);
}
make_paths ();
for (int i=1; i<=m; i++)
{
fin>>op>>x>>y;
operation (op,x,y);
}
}