#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#define MAXN 100005 ///Valoarea maxima a lui N
#define INFI 100005 ///Valoarea maxima a adancimi unui nod
#define bol bool ///Fixatie ca toate sa fie aliniate(bool are 4 litere, int are 3)
using namespace std;
///Arborele il construim din nodul 1
struct NOD{
int cost; ///Costul Nodului
int last; ///Ultima pozitie in sirul eulerian al nodului
int adan; ///Adancimea nodului
int pozi; ///Indica care este secventa din vectorul stdr care se afla nodul
int loca; ///Locatia la care de afla nodul in vectorul siru
int fath; ///Tatal nodului
bol hevy; ///Este true cand este un nod heavy
bol visi; ///Pentru Pargurgerea cu DFS
};
struct LFD{
int left; ///Limita Stanga
///int righ; ///Limita Dreapta --- neutilizat
};
vector <int> graf[MAXN];///Graful in sine
NOD poit[MAXN]; ///Nodurile Grafului
LFD stdr[MAXN]; ///Pozitiile intervalelor de Heavy Pathuri
int aint[MAXN*4]; ///Aint-ul pentru gasirea maximului din vectorul siru
int siru[MAXN]; ///Sirul format de secvente de Heavy Pathuri
int eule[MAXN*2]; ///Pargurgerea Euleriana a Arborelui
int alca[MAXN*8]; ///Aint-ul pentru determinarea LCA
int t;
int Min(int x,int y){
return x<y?x:y;
}
int Max(int x,int y){
return x>y?x:y;
}
int DFS(int node,int adan){
int ma,x,s,aux;
s=1;
ma=-1;///Folosim Maximul pentru determinarea care este copilul Heavy
x=-1; ///Indicele copilului Heavy
poit[node].adan=adan;
poit[node].visi=true;
poit[node].last=t;///Formam parcurgerea euleriana
eule[t++]=adan; ///Setam ultima pozitie la care se afla nodul in parcurgerea euleriana in acest moment
for(int neigh : graf[node]){ ///Pargurgem Fii
if(!poit[neigh].visi){
aux=DFS(neigh,adan+1);
poit[node].last=t; ///Formam parcurgerea euleriana
eule[t++]=adan; ///Setam ultima pozitie la care se afla nodul in parcurgerea euleriana in acest moment
if(ma<aux){ ///Determinam Heavy Child
ma=aux;
x=neigh;
}
s+=aux;
}
}
if(x!=-1){
poit[x].hevy=true; ///Setam un fiu ca Heavy Child
}
return s; ///Nr de noduri din acest subarbore
}
void CreateLCA(int x,int y,int poz){ ///Formam un Aint pentru sirul eulerian
int mij;
if(x==y){
alca[poz]=eule[x];
return;
}
mij=(x+y)/2;
CreateLCA(x ,mij,poz*2 );
CreateLCA(mij+1,y ,poz*2+1);
alca[poz]=Min(alca[poz*2],alca[poz*2+1]);
}
int LCA(int x,int y,int i,int j,int poz){ ///Determinam valoare minima din sirul eulerian de la i la j
int mij;
if(i<=x && y<=j){
return alca[poz];
}
if(y<i || j<x){
return INFI;
}
mij=(x+y)/2;
return Min(LCA(x ,mij,i,j,poz*2 ),
LCA(mij+1,y ,i,j,poz*2+1));
}
void MakeHeavyDecomp(int n){ ///Folosindune de copiii light formam un lant de la fiecare copil light la o frunza doar prin copii Heavy
int i,m,t,s,node;
for(i=1;i<=n;i++){ ///Setam ca nu am vizitat niciun nod
poit[i].visi=0;
}
m=0;
t=1;
for(i=1;i<=n;i++){
if(poit[i].hevy==0){
stdr[m].left=t; ///Setam pozitia de inceput a lantului de un copil Light si de restul Heavy
s=1;
node=i;
poit[node].visi=1;///Am visitat nodul
while(s==1){ ///Parcurgem pana nu mai sunt fii Heavy
poit[node].loca=t; ///Setam loca nodului cu pozitia nodului in sir
siru[t++]=node; ///Adaugam la sir acest nod
poit[node].pozi=m; ///Setam pozi nodului cu pozitia lantului de un copil Light si de restul Heavy
s=0;///Nu stim daca avem copil Heavy
for(int neigh : graf[node]){
if(poit[neigh].hevy && !poit[neigh].visi){
node=neigh;
poit[neigh].visi=1; ///Visitam nodul
s=1; ///Avem un fiu Heavy
}
}
}
m++; ///Trecem la urmatorul lant
}
}
}
void CreateAINT(int x,int y,int poz){///Formam un aint cu costurile nodurilor care se afla in vectorul siru
int mij;
if(x==y){
aint[poz]=poit[siru[x]].cost;
return;
}
mij=(x+y)/2;
CreateAINT(x ,mij,poz*2 );
CreateAINT(mij+1,y ,poz*2+1);
aint[poz]=Max(aint[poz*2],aint[poz*2+1]);
}
int SearchAINT(int x,int y,int i,int j,int poz){///Gasim maximul din aint de la i la j
int mij;
if(i<=x && y<=j){
return aint[poz];
}
if(j<x || y<i){
return 0;
}
mij=(x+y)/2;
return Max(SearchAINT(x ,mij,i,j,poz*2 ),
SearchAINT(mij+1,y ,i,j,poz*2+1));
}
void UpdateAINT(int x,int y,int i,int val,int poz){///Schimbam valoarea unei pozitiei i din aint cu val
int mij;
if(x==i && i==y){
aint[poz]=val;
return;
}
if(i<x || y<i){
return;
}
mij=(x+y)/2;
UpdateAINT(x ,mij,i,val,poz*2 );
UpdateAINT(mij+1,y ,i,val,poz*2+1);
aint[poz]=Max(aint[poz*2],aint[poz*2+1]);
}
int main(){
int n,m,i,x,y,inter,st,dr,r,j,aux,last;
FILE *fin,*fout;
fin=fopen("heavypath.in","r");
fout=fopen("heavypath.out","w");
fscanf(fin,"%d%d",&n,&m);
for(i=1;i<=n;i++){
fscanf(fin,"%d",&poit[i].cost);
poit[i].fath=-1; ///Initializam parinti nodurilor cu -1
}
for(i=1;i<n;i++){
fscanf(fin,"%d%d",&x,&y);
if(poit[y].fath==-1){ ///Setam parintele nodului
poit[y].fath=x;
}else{
poit[x].fath=y;
}
graf[x].push_back(y);
graf[y].push_back(x);
}
DFS(1,1); ///Pornim un DFS din nodul 1 cu adancimea 1
CreateLCA(1,2*n-2,1);
MakeHeavyDecomp(n);
CreateAINT(1,n,1);
for(i=0;i<m;i++){
fscanf(fin,"%d%d%d",&t,&x,&y);
if(t==0){
UpdateAINT(1,n,poit[x].loca,y,1); ///Schimbam valoarea
}else{
st=poit[x].last; ///Luam ultimile pozitii ale nodurilor x si y din parcurgerea euleriana
dr=poit[y].last;
if(st>dr){ ///Facem ca st sa fie intotdeauna mai mic ca dr
aux=dr;
dr=st;
st=aux;
}
inter=LCA(1,2*n-2,st,dr,1); ///Determinam inaltimea punctului de intersectie
r=-1; ///Initializam raspunsul cu -1(Nu ne afecteaza Maximul)
for(j=0;j<2;j++){ ///Generalizam cele doua parcurgeri din lant in lant pana la intersectie
if(j==0){
last=x; /// Mai intai facem cu x
}else{
last=y; /// Dupa facem cu y
}
///last este primul nod pe care vrem sa il parcurgem dar nu l-am parcurs
while(poit[siru[stdr[poit[last].pozi].left]].adan>inter){ ///Luam pozitia de start a intervalului in care se afla nodul last si verificam ca inaltimea este mai mare ca inaltimea intersectiei
r=Max(r,SearchAINT(1,n,stdr[poit[last].pozi].left,poit[last].loca,1)); ///Facem maximul dintre raspunsul pe care il avem si maximul din intervalul pozitiei de inceput al lantului in care se afla si nodul curent
last=poit[siru[stdr[poit[last].pozi].left]].fath;///Setam nodul nou care este primul, ca fiind tatal pozitiei de start al lantului in care se afla nodul last
}
r=Max(r,SearchAINT(1,n,stdr[poit[last].pozi].left+inter-poit[siru[stdr[poit[last].pozi].left]].adan,poit[last].loca,1)); /// Am ajuns sa nu mai parcurgem tot lantul astfel facem maximul din intervalul pozitie la care se afla intersectia si nodul last
///Determinam nodul la care se afla pozitia de intersectie folosind o formula luand pozitia de start a lantului in sir si adunand diferenta de inaltimi dintre ele, care ne aduce la pozitia de intersectie
}
fprintf(fout,"%d\n",r);
}
}
fclose(fin);
fclose(fout);
return 0;
}