#include <fstream>
#include <queue>
using namespace std;
ifstream cin ("heavypath.in");
ofstream cout ("heavypath.out");
struct Muchie { int nod_1 , nod_2 , urmatorul_1 , urmatorul_2; } muchii[100000];
int capat[100001] , valoare[100001] , lant[100001] , pozitie_lant[100001] , urmatorul[100001] , adancime[100001] , inceput[100001] , aparitii[100001] , arbore[400000];
queue <int> lant_actual;
void Build (const int termen , const int nod_actual , const int stanga , const int dreapta)
{
if (stanga == dreapta) {
arbore[termen + nod_actual] = valoare[lant_actual.front()];
lant_actual.pop(); arbore[0] = max(arbore[0] , termen + nod_actual);
return;
}
const int mijloc = (stanga + dreapta) >> 1;
Build(termen , nod_actual << 1 , stanga , mijloc);
Build(termen , (nod_actual << 1) | 1 , mijloc + 1 , dreapta);
arbore[termen + nod_actual] = max(arbore[termen + (nod_actual << 1)] , arbore[termen + ((nod_actual << 1) | 1)]);
}
int Distribuire (const int nod_actual , const int nod_sursa)
{
adancime[nod_actual] = adancime[nod_sursa] + 1;
int maxim = -1 , nod_maxim = 0 , cantitate = 1;
for (int indice = capat[nod_actual] ; indice ; )
{
const int nod_vecin = (muchii[indice].nod_1 == nod_actual ? muchii[indice].nod_2 : muchii[indice].nod_1);
if (nod_vecin != nod_sursa)
{
const int candidat = Distribuire(nod_vecin , nod_actual);
if (candidat > maxim)
{ maxim = candidat; nod_maxim = nod_vecin; }
cantitate += candidat;
}
if (nod_vecin == muchii[indice].nod_1) { indice = muchii[indice].urmatorul_2; }
else { indice = muchii[indice].urmatorul_1; }
}
if (maxim == -1) { lant[nod_actual] = ++lant[0]; }
else { lant[nod_actual] = lant[nod_maxim]; }
aparitii[lant[nod_actual]]++;
urmatorul[nod_actual] = nod_maxim;
return cantitate;
}
void Descompunere (const int nod_actual , const int nod_sursa)
{
lant_actual.push(nod_actual);
pozitie_lant[nod_actual] = (lant[nod_actual] == lant[nod_sursa] ? pozitie_lant[nod_sursa] + 1 : 1);
if (urmatorul[nod_actual]) { Descompunere(urmatorul[nod_actual] , nod_actual); }
else { inceput[lant[nod_actual]] = ++arbore[0]; Build(arbore[0] - 1 , 1 , 1 , aparitii[lant[nod_actual]]); }
for (int indice = capat[nod_actual] ; indice ; )
{
const int nod_vecin = (muchii[indice].nod_1 == nod_actual ? muchii[indice].nod_2 : muchii[indice].nod_1);
if (nod_vecin != nod_sursa && nod_vecin != urmatorul[nod_actual])
{ Descompunere(nod_vecin , nod_actual); }
if (nod_vecin == muchii[indice].nod_1) { indice = muchii[indice].urmatorul_2; }
else { indice = muchii[indice].urmatorul_1; }
}
}
void Stabilire (const int nod_actual , const int nod_sursa)
{
urmatorul[nod_actual] = (lant[nod_actual] == lant[nod_sursa] ? urmatorul[nod_sursa] : nod_sursa);
for (int indice = capat[nod_actual] ; indice ; )
{
const int nod_vecin = (muchii[indice].nod_1 == nod_actual ? muchii[indice].nod_2 : muchii[indice].nod_1);
if (nod_vecin != nod_sursa)
{ Stabilire(nod_vecin , nod_actual); }
if (nod_vecin == muchii[indice].nod_1) { indice = muchii[indice].urmatorul_2; }
else { indice = muchii[indice].urmatorul_1; }
}
}
void Update (const int termen , const int nod , const int stanga , const int dreapta , const int pozitie , const int update)
{
if (stanga == dreapta)
{ arbore[termen + nod] = update; return; }
const int mijloc = (stanga + dreapta) >> 1;
if (pozitie <= mijloc) { Update(termen , nod << 1 , stanga , mijloc , pozitie , update); }
else { Update(termen , (nod << 1) | 1 , mijloc + 1 , dreapta , pozitie , update); }
arbore[termen + nod] = max(arbore[termen + (nod << 1)] , arbore[termen + ((nod << 1) | 1)]);
}
int Query (const int termen , const int nod , const int stanga , const int dreapta , const int stanga_interval , const int dreapta_interval)
{
if (stanga_interval <= stanga && dreapta <= dreapta_interval)
{ return arbore[termen + nod]; }
int candidat[2] = { };
const int mijloc = (stanga + dreapta) >> 1;
if (stanga_interval <= mijloc) { candidat[0] = Query(termen , nod << 1 , stanga , mijloc , stanga_interval , dreapta_interval); }
if (dreapta_interval > mijloc) { candidat[1] = Query(termen , (nod << 1) | 1 , mijloc + 1 , dreapta , stanga_interval , dreapta_interval); }
return max(candidat[0] , candidat[1]);
}
bool vizitat[1000000];
void Parcurgere (const int termen , const int nod_actual , const int stanga , const int dreapta)
{
if (vizitat[nod_actual + termen])
{ cout << nod_actual + termen << ' '; exit(0); }
vizitat[nod_actual + termen] = true;
if (stanga == dreapta) return;
const int mijloc = (stanga + dreapta) / 2;
Parcurgere(termen , nod_actual * 2 , stanga , mijloc);
Parcurgere(termen , nod_actual * 2 + 1 , mijloc + 1 , dreapta);
}
int main ()
{
int numar_noduri , numar_operatii;
cin >> numar_noduri >> numar_operatii;
for (int indice = 1 ; indice <= numar_noduri ; indice++)
{ cin >> valoare[indice]; }
for (int indice = 1 ; indice < numar_noduri ; indice++)
{
cin >> muchii[indice].nod_1 >> muchii[indice].nod_2;
muchii[indice].urmatorul_1 = capat[muchii[indice].nod_1];
muchii[indice].urmatorul_2 = capat[muchii[indice].nod_2];
capat[muchii[indice].nod_1] = capat[muchii[indice].nod_2] = indice;
}
Distribuire(1 , 0);
Descompunere(1 , 0);
Stabilire(1 , 0);
while (numar_operatii--)
{
int tip;
cin >> tip;
switch (tip)
{
case 0:
{
int nod; cin >> nod >> valoare[nod];
Update(inceput[lant[nod]] - 1 , 1 , 1 , aparitii[lant[nod]] , pozitie_lant[nod] , valoare[nod]);
}
break;
case 1:
{
int nod_1 , nod_2;
cin >> nod_1 >> nod_2;
int valoare_maxima = 0;
while (lant[nod_1] != lant[nod_2]) {
if (adancime[urmatorul[nod_1]] > adancime[urmatorul[nod_2]]) { valoare_maxima = max(valoare_maxima , Query(inceput[lant[nod_1]] - 1 , 1 , 1 , aparitii[lant[nod_1]] , 1 , pozitie_lant[nod_1])); nod_1 = urmatorul[nod_1]; }
else { valoare_maxima = max(valoare_maxima , Query(inceput[lant[nod_2]] - 1 , 1 , 1 , aparitii[lant[nod_2]] , 1 , pozitie_lant[nod_2])); nod_2 = urmatorul[nod_2]; }
}
valoare_maxima = max(valoare_maxima , Query(inceput[lant[nod_1]] - 1 , 1 , 1 , aparitii[lant[nod_1]] , min(pozitie_lant[nod_1] , pozitie_lant[nod_2]) , max(pozitie_lant[nod_1] , pozitie_lant[nod_2])));
cout << valoare_maxima << '\n';
}
break;
}
}
cout.close(); cin.close();
return 0;
}