/*
Am alocat dinamic tree[][]
*/
/*
Sursa experimentala, inca nu sunt foarte convins de ce am facut
*/
#include <iostream>
#include <fstream>
#include <vector>
#define NMAX 100000 //o suta de mii
#define LOG_EUL 17
using namespace std;
ifstream fin ("heavypath.in");
ofstream fout ("heavypath.out");
int nrLanturi;
int dim_eul;
int smNod[NMAX + 1];
vector <int> v[NMAX + 1];
int val[NMAX + 1];
int lant[NMAX + 1];
int dimensiuneLant[NMAX + 1];
int posLant[NMAX + 1];
int tataLant[NMAX + 1];
int tt[NMAX + 1];
int **tree;
int eul[2 * NMAX];
int firstEul[NMAX + 1];
int RMQ[LOG_EUL][2 * NMAX];
/////////////////////
inline void adaugareEul(int node){
dim_eul++;
eul[dim_eul] = node;
}
/////////////////////
void DFS(int node, int tata){
//printf("DFS(%d, %d)\n", node, tata);
adaugareEul(node);
firstEul[node] = dim_eul;
smNod[node] = 1;
int MX = -1;
int fiuMx;
for(int i = 0; i < v[node].size(); i++){
int vec = v[node][i];
if(vec != tata){
DFS(vec, node);
smNod[node] += smNod[vec];
if(smNod[vec] > MX){
MX = smNod[vec];
fiuMx = vec;
}
adaugareEul(node);
}
else {
tt[node] = vec;
}
}
if(MX == -1){
//este frunza, creez un nou lant
nrLanturi++;
lant[node] = nrLanturi;
dimensiuneLant[nrLanturi] = 1;
tataLant[ lant[node] ] = node;
//printf("%d este frunza!\n", node);
}
else {
//adaug node la lantul din care face parte fiuMx
lant[node] = lant[fiuMx];
dimensiuneLant[ lant[node] ] ++;
tataLant[ lant[node] ] = node;
}
}
/////////////////////
void buildPos(int node, int tata){
if(lant[node] == lant[tata]){
posLant[node] = posLant[tata] + 1;
}
else {
posLant[node] = 1;
}
for(int i = 0; i < v[node].size(); i++){
int vec = v[node][i];
if(vec != tata){
buildPos(vec, node);
}
}
}
/////////////////////
void computeRMQ(){
for(int i = 1; i <= dim_eul; i++){
RMQ[0][i] = eul[i];
}
for(int j = 1; (1 << j) <= dim_eul; j++){
for(int i = 1; i - 1 + (1 << j) <= dim_eul; i++){
RMQ[j][i] = min(RMQ[j - 1][i], RMQ[j - 1][i + (1 << (j - 1))]);
}
}
}
/////////////////////
int putereDoiLowerbound(int X){
int pt = 0;
while( (1 << pt) <= X ){
pt++;
}
return pt - 1;
}
/////////////////////
int minimRMQ(int left, int right){
int k = putereDoiLowerbound(right - left + 1);
return min(RMQ[k][left], RMQ[k][right - (1 << k) + 1]);
}
/////////////////////
int lca(int A, int B){
if(firstEul[A] > firstEul[B]){
swap(A, B);
}
return minimRMQ(firstEul[A], firstEul[B]);
}
/////////////////////
int NR;
int POS;
int VAL;
int A, B;
/////////////////////
void updateTree(int node, int left, int right){
if(left == right){
tree[NR][node] = VAL;
return;
}
int mid = left + (right - left) / 2;
if(POS <= mid){
updateTree(node * 2, left, mid);
}
else {
updateTree(node * 2 + 1, mid + 1, right);
}
tree[NR][node] = max(tree[NR][node * 2], tree[NR][node * 2 + 1]);
}
/////////////////////
int queryTree(int node, int left, int right){
if(A <= left && right <= B){
return tree[NR][node];
}
int mid = left + (right - left) / 2;
int rez1 = -1, rez2 = -1;
if(A <= mid){
//A oricum e 1 mereu deci mereu se va intra in acest if
rez1 = queryTree(node * 2, left, mid);
}
if(B >= mid + 1){
rez2 = queryTree(node * 2 + 1, mid + 1, right);
}
return max(rez1, rez2);
}
/////////////////////
int maximPeLant(int JOS, int SUS){
int REZ = -1;
while(lant[JOS] != lant[SUS]){
//printf("JOS = %d\n", JOS);
//urc
NR = lant[JOS];
A = posLant[ tataLant[NR] ];
B = posLant[ JOS ];
/*
printf("REZ = %d\n", REZ);
printf("NR = %d\n", NR);
printf("queryTree(1, 1, %d)\n", dimensiuneLant[NR]);
*/
REZ = max( REZ, queryTree(1, 1, dimensiuneLant[NR]) );
/*
printf("REZ = %d\n", REZ);
printf("JOS = %d\n", JOS);
*/
JOS = tt[ tataLant[ lant[JOS] ] ];
//printf("JOS = %d\n", JOS);
}
//printf("sfarsit While!\n");
//acum au ajuns pe acelasi lant JOS si SUS
NR = lant[JOS];
A = posLant[ tataLant[NR] ];
B = posLant[ JOS ];
REZ = max( REZ, queryTree(1, 1, dimensiuneLant[NR]) );
//printf("Sfarsit maximPeLant!\n");
//printf("\n\n");
return REZ;
}
/////////////////////
int rezultat(int X, int Y){
int LCA = lca(X, Y);
//printf("lca(%d, %d) = %d\n", X, Y, LCA);
return max( maximPeLant(X, LCA), maximPeLant(Y, LCA) );
}
/////////////////////
void alocareDinamicMemorieTree(){
tree = (int **) malloc( (nrLanturi + 1) * sizeof(int *) );
for(int i = 1; i <= nrLanturi; i++){
tree[i] = (int *) malloc( (4 * dimensiuneLant[i] + 1) * sizeof(int) );
}
}
int main()
{
int N, M;
fin >> N >> M;
for(int i = 1; i <= N; i++){
fin >> val[i];
}
for(int i = 1; i <= N - 1; i++){
int x, y;
fin >> x >> y;
v[x].push_back(y);
v[y].push_back(x);
}
tt[1] = 1;
DFS(1, 1);
buildPos(1, 1);
computeRMQ();
alocareDinamicMemorieTree();
/*
for(int i = 1; i <= N; i++){
printf("lant[ %d ] = %d\n", i, lant[i]);
}
printf("\n\n");
printf("Ajuns!\n");
*/
for(int i = 1; i <= N; i++){
NR = lant[i];
POS = posLant[i];
VAL = val[i];
updateTree(1, 1, dimensiuneLant[NR]);
}
//printf("Ajuns!\n");
for(int i = 1; i <= M; i++){
//cout << "i = " << i << "\n";
int tip, x, y;
fin >> tip >> x >> y;
if(tip == 0){
NR = lant[x];
POS = posLant[x];
VAL = y;
updateTree(1, 1, dimensiuneLant[NR]);
}
else {
fout << rezultat(x, y) << "\n";
}
}
return 0;
}