#include <algorithm>
#include <cstdio>
#include <vector>
#include <list>
#define IN "heavypath.in"
#define OUT "heavypath.out"
#define NMax 100000
using namespace std;
int N, M;
// vals of nodes
vector<int> Val;
// adiacency list
vector<int> Nei[NMax];
// levels of nodes
vector<int> Lev;
// sizes of nodes
vector<int> Siz;
// chains of nodes
vector<int> Cha;
// first of chains
vector<int> Fir;
// boss of chain
vector<int> Bos;
// chains
vector< vector<int> > CSet;
// trees
vector< vector<int> > STree;
// get position
inline int position(const int &x)
{
// return position
return Lev[x] - Lev[Fir[Cha[x]]];
}
// query tree
int queryTree(const int &c, const int &n, const int &l, const int &r, const int &px, const int &py)
{
// is it outside the border?
if (px > r || py < l)
return -1;
// is it a contiguous node?
if (px <= l && r <= py)
return STree[c][n];
// get middle
int m = (l + r) >> 1;
// get the left and right values
int lv = queryTree(c, n << 1 | 1, l, m, px, py);
int rv = queryTree(c, (n + 1) << 1, m + 1, r, px, py);
// return the maximum
return max(lv, rv);
}
// change
inline void change (int &x, int &y)
{
int z;
z = x;
x = y;
y = z;
}
// query
int query(const int &x, const int &y)
{
// check for same node
if (x == y) return Val[x];
// get chains of x and y
int cx = Cha[x], cy = Cha[y];
// are they of the same chain?
if (cx == cy)
{
// get positions
int px = position(x), py = position(y);
// return the minimum
return queryTree(cx, 0, 0, CSet[cx].size() - 1, min(px, py), max(px, py));
}
// get qquery
int qquery = Lev[Bos[cx]] > Lev[Bos[cy]]?
query(Bos[cx], y):
query(Bos[cy], x);
// get tquery
int tquery = Lev[Bos[cx]] > Lev[Bos[cy]]?
queryTree(cx, 0, 0, CSet[cx].size() - 1, 0, position(x)):
queryTree(cy, 0, 0, CSet[cy].size() - 1, 0, position(y));
// return max
return max(qquery, tquery);
}
void updateTree(const int &c, const int &n, const int &l, const int &r, const int &p, const int &v)
{
// is it one element
if (l == r)
{
// set the value in this node
STree[c][n] = v;
// ok, done
return;
}
// get middle
int m = (l + r) >> 1;
// test for left
if (p <= m) updateTree(c, (n << 1) | 1, l, m, p, v);
// test for right
else updateTree(c, (n + 1) << 1, m + 1, r, p, v);
// set maximum value
STree[c][n] = max(STree[c][(n << 1) | 1], STree[c][(n + 1) << 1]);
}
// set value of x to y
void update(const int &x, const int &y)
{
// get chain of x
int c = Cha[x];
// update tree
updateTree(c, 0, 0, CSet[c].size() - 1, position(x), y);
}
// level, size, parent [node] with [parent] and [level]
void levelSizeParent(const int &n, const int &p, const int &l)
{
int c, s = 0, m = 0, r = 0, d = 0;
// set level, parent, size for current node
Lev[n] = l, Siz[n] = 1;
// is it a leaf?
if (Nei[n].size() == 1 && Nei[n][0] == p)
{
// get size
c = CSet.size();
// create a new chain
CSet.push_back(vector<int>(1, n));
// set chain of node
Cha[n] = c;
// set first node of chain
Fir.push_back(n);
// set boss node of chain
Bos.push_back(N);
// ok, done with that
return;
}
// iterate all neighbours
for (int i = 0; i < Nei[n].size(); ++i)
{
// get child
c = Nei[n][i];
// is it a child?
if (c != p)
{
// go to [child] with [node] and [level]
levelSizeParent(c, n, l + 1);
s += (d = Siz[c]);
// set size to maximum
if (d > m)
m = d, r = Cha[c];
}
}
// add node to chain
CSet[r].push_back(n);
// add childrens' sizes to node
Siz[n] += s;
// set first node of chain to n
Fir[r] = n;
// set chain of r as chain of n
Cha[n] = r;
// iterate all neighbours
for (int i = 0; i < Nei[n].size(); ++i)
{
// get child
c = Nei[n][i];
// is it a child with other chain?
if (c != p && Cha[c] != r)
Bos[Cha[c]] = n;
}
}
// build tree
void buildTree(const int &c, const int &n, const int &l, const int &r)
{
// is it one element
if (l == r)
{
// set the value in this node
STree[c][n] = Val[CSet[c][l]];
// ok, done
return;
}
// get middle
int m = (l + r) >> 1;
// set for child nodes
buildTree(c, (n << 1) | 1, l, m);
buildTree(c, (n + 1) << 1, m + 1, r);
// set maximum value
STree[c][n] = max(STree[c][(n << 1) | 1], STree[c][(n + 1) << 1]);
}
// build a segment tree
void buildSegmentTree(const int &c)
{
// set tree length
int s = CSet[c].size();
s <<= 1;
// test 2n
s & (s - 1)?
// add a sized vector
STree.push_back(vector<int>((s & (s - 1)) << 1, 0)):
// add a sized vector
STree.push_back(vector<int>(s, 0));
buildTree(c, 0, 0, CSet[c].size() - 1);
}
int main()
{
int i, a, b;
freopen(IN, "r", stdin);
freopen(OUT, "w", stdout);
// read size
scanf("%d%d", &N, &M);
// read values O(N)
Val.assign(N, -1);
for (i = 0; i < N; ++i)
scanf("%d", &Val[i]);
// read edges O(N)
for (i = 1; i < N; ++i)
{
scanf("%d%d", &a, &b);
--a, --b;
Nei[a].push_back(b);
Nei[b].push_back(a);
}
// initialize L, P, S arrays O(N)
Lev.assign(N, 0);
Siz.assign(N, 0);
Cha.assign(N, -1);
// level, size, parent nodes O(N)
levelSizeParent(0, -1, 0);
Lev.push_back(-1);
// reverse all chains to have elements by level O(NlogN)
for (i = 0; i < CSet.size(); ++i)
{
reverse(CSet[i].begin(), CSet[i].end());
buildSegmentTree(i);
}
// do all jobs
while (M--)
{
scanf("%d%d%d", &i, &a, &b);
// for the type of question
i?
// query in O(log(N))
printf("%d\n", query(a - 1, b - 1)):
// update in O(log(N))
update(i = a - 1, b);
}
return 0;
}