#include <fstream>
#include <vector>
using namespace std;
///Aceasta este solutia unde din radacina pana intr-o frunza oarecare trec prin maxim sqrt(N) lanturi distincte, deoarece aici aleg pe ce nod cobor dupa adancimea maxima din subarborele nodului.
///O sa pun prima solutie ca fiind cea mai eficienta, in O(M * log(N) * log(N)), unde aleg pe ce nod cobor in functie de cat de "greu" e acel nod, adica dupa dimensiunea subarborelui nodului, nu dupa adancimea maxima. Asa voi trece prin maxim log(N) lanturi intre radacina si frunza, mai putin adica.
///Aceasta este solutia putin mai ineficienta, in O(M * sqrt(N) * log(N)). Mai eficienta este in O(M * log(N) * log(N)).
class Parser
{
private:
static const int BUFF_SIZE = 5000000;
ifstream in;
char buffer[BUFF_SIZE];
int posBuff;
public:
Parser(char* file)
{
in = ifstream(file);
posBuff = BUFF_SIZE - 1;
ios_base::sync_with_stdio(false);
in.tie(NULL);
}
int getInt()
{
int rez = 0;
while (!('0' <= buffer[posBuff] && buffer[posBuff] <= '9'))
{
posBuff++;
if (posBuff == BUFF_SIZE)
{
posBuff = 0;
in.read(buffer, BUFF_SIZE);
}
}
while ('0' <= buffer[posBuff] && buffer[posBuff] <= '9')
{
rez = rez * 10 + buffer[posBuff] - '0';
posBuff++;
if (posBuff == BUFF_SIZE)
{
posBuff = 0;
in.read(buffer, BUFF_SIZE);
}
}
return rez;
}
};
Parser input("heavypath.in");
const int NMAX = 100000;
const int LOGMAX = 17;
int v[1 + NMAX];
vector<int> graf[1 + NMAX];
bool vizitatCostJos[1 + NMAX];
int costJos[1 + NMAX];
bool vizitatLanturi[1 + NMAX];
vector<vector<int>> lanturi;
int indexLantNod[1 + NMAX];
int pozLantNod[1 + NMAX];
vector<vector<int>> arboriLanturi;
pair<int, int> tataLant[1 + NMAX];
bool vizitatLCA[1 + NMAX];
vector<int> liniarizareLCA;
int primaAparitieLCA[1 + NMAX];
int adancimeNod[1 + NMAX];
int log2[1 + 2 * NMAX];
pair<int, int> rmq[1 + 2 * NMAX][1 + LOGMAX];
void dfsCostJos(int nod)
{
vizitatCostJos[nod] = true;
costJos[nod] = 1;
for (int i = 0; i < graf[nod].size(); i++)
{
int fiu = graf[nod][i];
if (!vizitatCostJos[fiu])
{
dfsCostJos(fiu);
costJos[nod] = max(costJos[nod], costJos[fiu] + 1);
}
}
}
void dfsLCA(int nod)
{
vizitatLCA[nod] = true;
liniarizareLCA.push_back(nod);
primaAparitieLCA[nod] = (int)liniarizareLCA.size() - 1;
adancimeNod[nod] = 1;
for (int i = 0; i < graf[nod].size(); i++)
{
int fiu = graf[nod][i];
if (!vizitatLCA[fiu])
{
dfsLCA(fiu);
liniarizareLCA.push_back(nod);
adancimeNod[nod] = max(adancimeNod[nod], adancimeNod[fiu] + 1);
}
}
}
void dfsLanturi(int nod)
{
vizitatLanturi[nod] = true;
lanturi.back().push_back(nod);
int indexLantCurent = (int)lanturi.size() - 1;
int pozLantCurent = (int)lanturi.back().size() - 1;
int nodBunJos = -1;
int costNodBunJos = -1;
for (int i = 0; i < graf[nod].size(); i++)
{
int fiu = graf[nod][i];
if (!vizitatLanturi[fiu])
{
if (costJos[fiu] > costNodBunJos)
{
nodBunJos = fiu;
costNodBunJos = costJos[fiu];
}
}
}
if (nodBunJos != -1)
dfsLanturi(nodBunJos);
for (int i = 0; i < graf[nod].size(); i++)
{
int fiu = graf[nod][i];
if (!vizitatLanturi[fiu] && fiu != nodBunJos)
{
lanturi.emplace_back();
lanturi.back().push_back(0);
tataLant[(int)lanturi.size() - 1].first = indexLantCurent;
tataLant[(int)lanturi.size() - 1].second = pozLantCurent;
dfsLanturi(fiu);
}
}
}
void buildAint(int indexLanturi, int index, int st, int dr)
{
if (st == dr)
{
arboriLanturi[indexLanturi][index] = v[lanturi[indexLanturi][st]];
return;
}
int mij = (st + dr) / 2;
int fiuSt = 2 * index;
int fiuDr = 2 * index + 1;
buildAint(indexLanturi, fiuSt, st, mij);
buildAint(indexLanturi, fiuDr, mij + 1, dr);
arboriLanturi[indexLanturi][index] = max(arboriLanturi[indexLanturi][fiuSt], arboriLanturi[indexLanturi][fiuDr]);
}
void updateAint(int indexLanturi, int index, int st, int dr, int poz, int val)
{
if (st == dr)
{
arboriLanturi[indexLanturi][index] = val;
return;
}
int mij = (st + dr) / 2;
int fiuSt = 2 * index;
int fiuDr = 2 * index + 1;
if (poz <= mij)
updateAint(indexLanturi, fiuSt, st, mij, poz, val);
else
updateAint(indexLanturi, fiuDr, mij + 1, dr, poz, val);
arboriLanturi[indexLanturi][index] = max(arboriLanturi[indexLanturi][fiuSt], arboriLanturi[indexLanturi][fiuDr]);
}
int queryAint(int indexLanturi, int index, int st, int dr, int stQ, int drQ)
{
if (st == stQ && drQ == dr)
{
return arboriLanturi[indexLanturi][index];
}
int mij = (st + dr) / 2;
int fiuSt = 2 * index;
int fiuDr = 2 * index + 1;
if (drQ <= mij)
return queryAint(indexLanturi, fiuSt, st, mij, stQ, drQ);
else if (stQ >= mij + 1)
return queryAint(indexLanturi, fiuDr, mij + 1, dr, stQ, drQ);
else
return max(queryAint(indexLanturi, fiuSt, st, mij, stQ, mij), queryAint(indexLanturi, fiuDr, mij + 1, dr, mij + 1, drQ));
}
void preCalcRMQ()
{
for (int i = 2; i < liniarizareLCA.size(); i++)
log2[i] = log2[i / 2] + 1;
for (int i = 1; i < liniarizareLCA.size(); i++)
{
rmq[i][0].first = adancimeNod[liniarizareLCA[i]];
rmq[i][0].second = liniarizareLCA[i];
}
for (int l = 1; (1 << l) < liniarizareLCA.size(); l++)
{
for (int i = 1; i + (1 << l) - 1 < liniarizareLCA.size(); i++)
{
if (rmq[i][l - 1].first > rmq[i + (1 << (l - 1))][l - 1].first)
{
rmq[i][l].first = rmq[i][l - 1].first;
rmq[i][l].second = rmq[i][l - 1].second;
}
else
{
rmq[i][l].first = rmq[i + (1 << (l - 1))][l - 1].first;
rmq[i][l].second = rmq[i + (1 << (l - 1))][l - 1].second;
}
}
}
}
int calcLCA(int x, int y)
{
if (primaAparitieLCA[x] > primaAparitieLCA[y])
swap(x, y);
int st = primaAparitieLCA[x];
int dr = primaAparitieLCA[y];
int logaritm2 = log2[dr - st + 1];
if (rmq[st][logaritm2].first > rmq[dr - (1 << logaritm2) + 1][logaritm2].first)
return rmq[st][logaritm2].second;
else
return rmq[dr - (1 << logaritm2) + 1][logaritm2].second;
}
int calcSol(int jos, int sus)
{
int sol = -1;
while (jos != sus)
{
if (indexLantNod[jos] == indexLantNod[sus])
{
sol = max(sol, queryAint(indexLantNod[jos], 1, 1, (int)lanturi[indexLantNod[jos]].size() - 1, pozLantNod[sus], pozLantNod[jos]));
jos = sus;
}
else
{
sol = max(sol, queryAint(indexLantNod[jos], 1, 1, (int)lanturi[indexLantNod[jos]].size() - 1, 1, pozLantNod[jos]));
jos = lanturi[tataLant[indexLantNod[jos]].first][tataLant[indexLantNod[jos]].second];
}
}
return sol;
}
int main()
{
ofstream out("heavypath.out");
int n, m;
n = input.getInt();
m = input.getInt();
for (int i = 1; i <= n; i++)
v[i] = input.getInt();
for (int i = 1; i <= n - 1; i++)
{
int a, b;
a = input.getInt();
b = input.getInt();
graf[a].push_back(b);
graf[b].push_back(a);
}
dfsCostJos(1);
lanturi.emplace_back();
lanturi.emplace_back();
lanturi.back().push_back(0);
tataLant[0].first = -1;
tataLant[0].second = -1;
tataLant[1].first = -1;
tataLant[1].second = -1;
dfsLanturi(1);
for (int i = 1; i < lanturi.size(); i++)
{
for (int j = 1; j < lanturi[i].size(); j++)
{
indexLantNod[lanturi[i][j]] = i;
pozLantNod[lanturi[i][j]] = j;
}
}
arboriLanturi.resize(lanturi.size());
for (int i = 1; i < lanturi.size(); i++)
arboriLanturi[i].resize(4 * (int)lanturi[i].size());
for (int i = 1; i < lanturi.size(); i++)
buildAint(i, 1, 1, (int)lanturi[i].size() - 1);
liniarizareLCA.push_back(0);
dfsLCA(1);
preCalcRMQ();
for (int i = 1; i <= m; i++)
{
int t, x, y;
t = input.getInt();
x = input.getInt();
y = input.getInt();
if (t == 0)
updateAint(indexLantNod[x], 1, 1, (int)lanturi[indexLantNod[x]].size() - 1, pozLantNod[x], y);
else
{
int stramos = calcLCA(x, y);
out << max(calcSol(x, stramos), calcSol(y, stramos)) << '\n';
}
}
return 0;
}