#include <fstream>
#include <vector>
/// asta e varianta unde heavy child-ul este ales dupa adancimea maxima a subarborelui
/// n * sqrt n * log n
using namespace std;
const int NMAX = 1e5;
int val[1 + NMAX];
vector<int> graf[1 + NMAX];
bool vizitat[1 + NMAX];
int adancime[1 + NMAX];
vector<int> liniarizare;
int primaAparitie[1 + NMAX];
const int LMAX = 2 * NMAX - 1;
int aintLCA[1 + 4 * LMAX];
int heaviness[1 + NMAX]; /// ori dimensiunea subarborelui, ori lungimea maxima a unui lant ce pleaca din radacina subarborelui in jos
/// cica daca fac cu lungimea maxima a unui lant atunci ar fi complexitate n * sqrt n * log n in loc de n * log n * log n pentru dimensiune subarbore, deci putin mai slab.
int indexPath[1 + NMAX];
int indexInLiniarizarePaths[1 + NMAX]; /// este pozitie globala in singura liniarizare pe toate path-urile simultan (+ aint)
/// am facut lca-ul cu aint in loc de rmq ca sa castig niste memorie ca e tight rau la problema asta
/// nu conteaza aici ca oricum complexitatea finala e n * log n * log n indiferent daca e rmq sau aint.
struct Path
{
public:
int indexTataPath;
int indexTataNodPath;
int indexInceput;
Path() :
indexTataPath(-1), indexTataNodPath(-1), indexInceput(-1)
{
}
};
vector<Path> paths;
vector<int> liniarizarePaths;
vector<int> aintLiniarizarePaths;
void dfs(int nod, int adanc)
{
vizitat[nod] = true;
adancime[nod] = adanc;
liniarizare.emplace_back(nod);
primaAparitie[nod] = (int)liniarizare.size() - 1;
heaviness[nod] = 1;
for (int i = 0; i < graf[nod].size(); ++i)
{
if (!vizitat[graf[nod][i]])
{
dfs(graf[nod][i], adanc + 1);
liniarizare.emplace_back(nod);
/// heaviness[nod] += heaviness[graf[nod][i]]; ///
heaviness[nod] = max(heaviness[nod], heaviness[graf[nod][i]] + 1);
}
}
/// ++heaviness[nod]; ///
}
void buildAintLCA(int index, int st, int dr)
{
if (st == dr)
{
aintLCA[index] = liniarizare[st];
return;
}
int mij = (st + dr) / 2;
int fiuSt = 2 * index;
int fiuDr = 2 * index + 1;
buildAintLCA(fiuSt, st, mij);
buildAintLCA(fiuDr, mij + 1, dr);
if (adancime[aintLCA[fiuSt]] < adancime[aintLCA[fiuDr]])
aintLCA[index] = aintLCA[fiuSt];
else
aintLCA[index] = aintLCA[fiuDr];
}
int queryAintLCA(int index ,int st, int dr, int stQ, int drQ)
{
if (st == stQ && drQ == dr)
return aintLCA[index];
int mij = (st + dr) / 2;
int fiuSt = 2 * index;
int fiuDr = 2 * index + 1;
if (drQ <= mij)
return queryAintLCA(fiuSt, st, mij, stQ, drQ);
else if (stQ >= mij + 1)
return queryAintLCA(fiuDr, mij + 1, dr, stQ, drQ);
else
{
int sol1 = queryAintLCA(fiuSt, st, mij, stQ, mij);
int sol2 = queryAintLCA(fiuDr, mij + 1, dr, mij + 1, drQ);
if (adancime[sol1] < adancime[sol2])
return sol1;
else
return sol2;
}
}
int LCA(int nod1, int nod2)
{
if (primaAparitie[nod1] > primaAparitie[nod2])
swap(nod1, nod2);
return queryAintLCA(1, 1, (int)liniarizare.size() - 1, primaAparitie[nod1], primaAparitie[nod2]);
}
void dfsPath(int nod)
{
vizitat[nod] = true;
liniarizarePaths.emplace_back(nod);
indexPath[nod] = (int)paths.size() - 1;
indexInLiniarizarePaths[nod] = (int)liniarizarePaths.size() - 1;
int maxHeaviness = 0;
int indexFiuMaxHeaviness = -1;
for (int i = 0; i < graf[nod].size(); ++i)
{
if (!vizitat[graf[nod][i]])
{
if (maxHeaviness < heaviness[graf[nod][i]])
{
maxHeaviness = heaviness[graf[nod][i]];
indexFiuMaxHeaviness = i;
}
}
}
if (indexFiuMaxHeaviness != -1)
{
swap(graf[nod][0], graf[nod][indexFiuMaxHeaviness]);
dfsPath(graf[nod][0]);
}
for (int i = 1; i < graf[nod].size(); ++i)
{
if (!vizitat[graf[nod][i]])
{
paths.emplace_back();
paths.back().indexTataPath = indexPath[nod];
paths.back().indexTataNodPath = nod;
paths.back().indexInceput = (int)liniarizarePaths.size() - 1 + 1;
dfsPath(graf[nod][i]);
}
}
}
void buildAintLiniarizarePaths(int index, int st, int dr)
{
if (st == dr)
{
aintLiniarizarePaths[index] = val[liniarizarePaths[st]];
return;
}
int mij = (st + dr) / 2;
int fiuSt = 2 * index;
int fiuDr = 2 * index + 1;
buildAintLiniarizarePaths(fiuSt, st, mij);
buildAintLiniarizarePaths(fiuDr, mij + 1, dr);
aintLiniarizarePaths[index] = max(aintLiniarizarePaths[fiuSt], aintLiniarizarePaths[fiuDr]);
}
void updateAintLiniarizarePaths(int index, int st, int dr, int poz, int val)
{
if (st == dr)
{
aintLiniarizarePaths[index] = val;
return;
}
int mij = (st + dr) / 2;
int fiuSt = 2 * index;
int fiuDr = 2 * index + 1;
if (poz <= mij)
updateAintLiniarizarePaths(fiuSt, st, mij, poz, val);
else
updateAintLiniarizarePaths(fiuDr, mij + 1, dr, poz, val);
aintLiniarizarePaths[index] = max(aintLiniarizarePaths[fiuSt], aintLiniarizarePaths[fiuDr]);
}
int queryAintLiniarizarePaths(int index, int st, int dr, int stQ, int drQ)
{
if (st == stQ && drQ == dr)
return aintLiniarizarePaths[index];
int mij = (st + dr) / 2;
int fiuSt = 2 * index;
int fiuDr = 2 * index + 1;
if (drQ <= mij)
return queryAintLiniarizarePaths(fiuSt, st, mij, stQ, drQ);
else if (stQ >= mij + 1)
return queryAintLiniarizarePaths(fiuDr, mij + 1, dr, stQ, drQ);
else
return max(queryAintLiniarizarePaths(fiuSt, st, mij, stQ, mij), queryAintLiniarizarePaths(fiuDr, mij + 1, dr, mij + 1, drQ));
}
int solutiePeLant(int x, int y) /// y e undeva mai sus de x => indexInLiniarizarePaths[y] <= indexInLiniarizarePaths[x] mereu
{
int sol = val[y];
while (x != y)
{
if (indexPath[x] == indexPath[y])
{
sol = max(sol, queryAintLiniarizarePaths(1, 1, (int)liniarizarePaths.size() - 1, indexInLiniarizarePaths[y], indexInLiniarizarePaths[x]));
x = y;
}
else
{
sol = max(sol, queryAintLiniarizarePaths(1, 1, (int)liniarizarePaths.size() - 1, paths[indexPath[x]].indexInceput, indexInLiniarizarePaths[x]));
x = paths[indexPath[x]].indexTataNodPath;
}
}
return sol;
}
int main()
{
ifstream in("heavypath.in");
ofstream out("heavypath.out");
ios_base::sync_with_stdio(false);
in.tie(nullptr);
int n, m;
in >> n >> m;
for (int i = 1; i <= n; ++i)
in >> val[i];
for (int i = 2; i <= n; ++i)
{
int a, b;
in >> a >> b;
graf[a].emplace_back(b);
graf[b].emplace_back(a);
}
liniarizare.reserve(1 + LMAX);
liniarizare.emplace_back(0);
dfs(1, 1);
buildAintLCA(1, 1, (int)liniarizare.size() - 1);
for (int i = 1; i <= n; ++i) /// nu am mai luat alt vector vizitat si pentru constructia path-urilor (tot pentru a castiga memorie)
vizitat[i] = false;
paths.reserve(1 + NMAX);
liniarizarePaths.reserve(1 + NMAX);
aintLiniarizarePaths.resize(1 + 4 * NMAX);
liniarizarePaths.emplace_back(-1);
paths.emplace_back();
paths.emplace_back();
paths.back().indexTataPath = -1;
paths.back().indexTataNodPath = -1;
paths.back().indexInceput = (int)liniarizarePaths.size() - 1 + 1;
dfsPath(1);
buildAintLiniarizarePaths(1, 1, n);
for (int i = 1; i <= m; ++i)
{
int tip, x, y;
in >> tip >> x >> y;
if (tip == 0) /// update
{
val[x] = y;
updateAintLiniarizarePaths(1, 1, (int)liniarizarePaths.size() - 1, indexInLiniarizarePaths[x], y);
}
else /// query
{
int lca = LCA(x, y);
out << max(solutiePeLant(x, lca), solutiePeLant(y, lca)) << '\n';
}
}
in.close();
out.close();
return 0;
}