using namespace std;
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cctype>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#include <array>
#include <sys/time.h>
#include <random>
#include <chrono>
#include <unordered_map>
#include <unordered_set>
#include <chrono>
using namespace std::chrono;
//#define DEBUG // general information//
template<class T, class V> std::ostream& operator <<(std::ostream& stream, const pair<T,V> & p) {
stream << p.first << "," << p.second << " ";
return stream;
}
template<class T> std::ostream& operator <<(std::ostream& stream, const vector<T> & v) {
for (auto el : v)
stream << el << " ";
return stream;
}
template<class T> std::ostream& operator <<(std::ostream& stream, const vector<vector<T>> & v) {
for (auto line : v) {
for (auto el : line)
stream << el << " ";
stream << "\n";
}
return stream;
}
class debugger {
public:
template<class T> void output (const T& v) {
cerr << v << " ";
}
template<class T> debugger& operator , (const T& v) {
output(v);
return *this;
}
} dbg;
////////////////
// templates //
////////////////
// general
//template size
template<typename T> int size(const T& c) { return int(c.size()); }
//template abs
template<typename T> T abs(T x) { return x < 0 ? -x : x; }
//template sqr
template<typename T> T sqr(T x) { return x*x; }
//template remin
template<typename T> bool remin(T& x, T y) { if (x <= y) return false; x = y; return true; }
//template remax
template<typename T> bool remax(T& x, T y) { if (x >= y) return false; x = y; return true; }
//template factorize
template<class T> inline vector<pair<T,int> > factorize(T n) {vector<pair<T,int> > R;for (T i=2;n>1;){if (n%i==0){int C=0;for (;n%i==0;C++,n/=i);R.push_back(make_pair(i,C));} i++;if (i>n/i) i=n;}if (n>1) R.push_back(make_pair(n,1));return R;}
//template toStr
template<typename T> string toStr(T x) { stringstream ss; ss << x; return ss.str(); }
//helper len
inline int len(const string & x){ return x.length(); }
//helper print_mask
inline void print_mask(int mask, int n){for (int i = 0; i < n; ++i) cerr << (char)('0' + (mask >> i & 1));}
//helper stoi
//int stoi(const string & s){stringstream ss(s); int res; ss >> res; return res;}
//helper itos
string itos(const int & x){stringstream ss; ss << x; return ss.str();}
// types
//template int64
typedef long long int64 ;
//template uint64
typedef unsigned long long uint64 ;
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef int64 hash_type;
// shortcuts
#define all(_xx) _xx.begin(), _xx.end()
#define pb push_back
#define vl vector<long long>
#define vs vector<string>
#define vvi vector<vector<int>>
#define vvl vector<vector<long long>>
#define vd vector<double>
#define vc vector<char>
#define vvc vector<vector<unsigned char>>
#define vpdd vector<pair<double,double> >
#define vi vector<int>
#define vpii vector<pair<int,int>>
//#define vpii vector<pair<short,short> >
#define pii pair<int, int>
#define pcc pair<char, char>
#define pucc pair<uchar, uchar>
#define pll pair<long long, long long>
#define pdd pair<double, double>
#define mp(XX, YY) make_pair(XX, YY)
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define SS stringstream
#define ptype unsigned short
// for loops
#define re(II, NN) for (int II(0), _NN(NN); (II) < (_NN); ++(II))
#define fod(II, XX, YY) for (int II(XX), _YY(YY); (II) >= (_YY); --(II))
#define fo(II, XX, YY) for (int II(XX), _YY(YY); (II) <= (_YY); ++(II))
#define foreach(it,c) for(__typeof((c).begin()) it=(c).begin();it!=(c).end();++it)
// Useful hardware instructions
#define bitcount __builtin_popcount
#define gcd __gcd
// Useful all around
#define checkbit(n,b) ((n >> (b)) & 1)
#define pt2(b) (1LL<<(b))
#define DREP(a) sort(a.begin(), a.end());a.erase(unique(a.begin(), a.end()),a.end())
#define INDEX(arr,ind) (lower_bound(arr.begin(), arr.end(),ind)-arr.begin())
#define PAUSE cerr << "Press any key to continue ..."; char xxxx; scanf("%c", &xxxx);
#define v_fill(xx,val) fill(all(xx), val)
#define m_fill(xx,val) memset(xx, val, sizeof(xx))
#define SUM(vv) accumulate(vv.begin(), vv.end(), 0)
#define oo (10001)
#define ALWAYS_INLINE inline __attribute__((always_inline))
#define ALIGNED __attribute__ ((aligned(16)))
#define likely(x) __builtin_expect(!!(x),1)
#define unlikely(x) __builtin_expect(!!(x),0)
struct Timer {
steady_clock::time_point ticks;
const string name;
bool running;
int count;
Timer() {;}
Timer(string _name) : name(_name) {
count = 0;
running = false;
}
double elapsed() {
auto current = steady_clock::now();
duration<double> time_span = duration_cast<duration<double>>(current - ticks);
return time_span.count();
}
void startTimer() {
if (running) return;
ticks = steady_clock::now();
running = true;
}
void checkTimer() {
double delta = elapsed();
cerr << name << ": " << delta << " count: " << count;
if (count) cerr << " mean per sec: " << (1 / delta) * count;
cerr << endl;
}
};
mt19937 random_engine;
ALWAYS_INLINE double nextDouble() {return 1.0 * (random_engine() % (1<<30) + 1) / ((1<<30)-1);}
ALWAYS_INLINE int nextInt() {return random_engine();}
ALWAYS_INLINE int nextInt(int range) {return random_engine() % range;}
ALWAYS_INLINE int nextInt(int first, int last) {return random_engine() % (last - first + 1) + first;}
// ----------------------------------------------------------------------------------------------------------------------------------------------- //
//#define LOCAL
//#define DEBUG
#ifdef LOCAL
#define WARN
#define INFO
#define HIGHLIGHT
#define DEBUG
#endif
//#define WARN
//#define HIGHLIGHT
#ifdef DEBUG
#define debug(args...) {dbg,args; cerr<<"\n"; cerr.flush();}
#else
#define debug(args...)
#endif
#ifdef WARN
#define warn(args...) {dbg,args; cerr<<endl; cerr.flush();}
#else
#define warn(args...)
#endif
#ifdef INFO
#define info(args...) {cerr << blue; dbg,args; cerr<< def << "\n"; cerr.flush();}
#else
#define info(args...)
#endif
#ifdef HIGHLIGHT
#define highlight(args...) {cerr << red; dbg,args; cerr<< def << "\n"; cerr.flush();}
#else
#define highlight(args...)
#endif
#define CRITICAL_TIME 0.5
#ifdef LOCAL
#define MAX_TIME 3.0
#define TIME_BUFFER 0.1
#else
#define MAX_TIME 3.0
#define TIME_BUFFER 0.1
#endif
struct Scanner {
char* curPos;
const static int BUF_SIZE = (2000000);
char input[BUF_SIZE];
FILE * fin;
void init(FILE * _fin) {
fin = _fin;
fread(input, 1, sizeof(input), fin);
curPos = input;
}
Scanner() {;}
inline void ensureCapacity() {
int size = input + BUF_SIZE - curPos;
if (size < 100) {
memcpy(input, curPos, size);
fread(input + size, 1, BUF_SIZE - size, fin);
curPos = input;
}
}
inline int nextInt() {
ensureCapacity();
while (!isdigit(*curPos) && *curPos != '-')
++curPos;
int res = 0;
int sign = 1;
if (*curPos == '-')
sign = -1,
++curPos;
while (*curPos >= '0' && *curPos <= '9')
res = res * 10 + (*(curPos++) & 15);
return sign * res;
}
} Sc;
//Timer solution_timer("solution");
#define MAX_N 100111
int N, M, A[MAX_N];
vvi g;
// handles lca and get_route as well
class HeavyPathDecomposition {
public:
// Color = id of chains
// Memory : 11 vi (N)
vi colors, positions; //Vertex -> Color, Vertex -> Offset
vi lengths, parents, branches; //Color -> Int, Color -> Color, Color -> Offset
vi parentnodes, depths; //Vertex -> Vertex, Vertex -> Int
vi sortednodes, offsets; //Index -> Vertex, Color -> Index
vi lefts, rights; //Vertex -> Index
int C;
struct BuildDFSState {
int n, len, parent;
BuildDFSState() { }
BuildDFSState(int n_, int l, int p): n(n_), len(l), parent(p) { }
};
void build (const vvi & g, int root) {
int n = g.size();
colors.assign(n, -1); positions.assign(n, -1);
lengths.clear(); parents.clear(); branches.clear();
parentnodes.assign(n, -1); depths.assign(n, -1);
sortednodes.clear(); offsets.clear();
lefts.assign(n, -1); rights.assign(n, -1);
vi subtreesizes;
// compute subtree sizes
measure(g, root, subtreesizes);
typedef BuildDFSState State;
depths[root] = 0;
stack<State> st;
st.push(State(root, 0, -1));
while (!st.empty()) {
State t = st.top(); st.pop();
int n = t.n, len = t.len;
int index = size(sortednodes);
int color = size(lengths);
if (t.parent == -3) {
rights[n] = index;
continue;
}
if(t.parent != -2) {
assert(size(parents) == color);
parents.pb(t.parent);
branches.pb(len);
offsets.pb(index);
len = 0;
}
colors[n] = color;
positions[n] = len;
lefts[n] = index;
sortednodes.pb(n);
int max_size = -1, max_v = -1;
for (const auto & v : g[n]) if (colors[v] == -1) {
if (max_size < subtreesizes[v]) {
max_size = subtreesizes[v];
max_v = v;
}
parentnodes[v] = n;
depths[v] = depths[n] + 1;
}
st.push(State(n, -1, -3));
if (max_v == -1) {
lengths.pb(len + 1);
} else {
for (const auto & v : g[n]) if (colors[v] == -1 && v != max_v)
st.push(State(v, len, color));
st.push(State(max_v, len + 1, -2));
}
}
C = size(lengths);
}
// id of chain and parent vertex of chain
void get(int v, int &c, int &p) const {
c = colors[v]; p = positions[v];
}
// go up to next chain
bool go_up(int &c, int &p) const {
p = branches[c]; c = parents[c];
return c != -1;
}
inline const int *nodesBegin(int c) const { return &sortednodes[0] + offsets[c]; }
inline const int *nodesEnd(int c) const { return &sortednodes[0] + (c+1 == size(offsets) ? size(sortednodes) : offsets[c+1]); }
// lca by going up chains at a time until they are on the same chain
int lowest_common_ancestor(int x, int y) {
int cx, px, cy, py;
get(x, cx, px);
get(y, cy, py);
while(cx != cy) {
if (depths[*nodesBegin(cx)] < depths[*nodesBegin(cy)])
go_up(cy, py);
else
go_up(cx, px);
}
return nodesBegin(cx)[min(px, py)];
}
typedef vector<pair<int,pii> > chains;
void get_route(int u, int v, chains &route) {
route.clear();
int w = lowest_common_ancestor(u, v), wc, wp;
get(w, wc, wp);
re (uv, 2) {
int c, p;
get(uv == 0 ? u : v, c, p);
int sz = size(route);
while (1) {
int top = c == wc ? wp + uv : 0;
//pii q = uv == 0 ? mp(p + 1, top) : mp(top, p + 1);
pii q = uv == 0 ? mp(p + 1, top) : mp(top, p + 1);
if (q.first != q.second) {
if (uv == 1 && c == wc) {
//assert(route[sz-1].first == c);
//assert(route[sz-1].second.first - route[sz-1].second.second == 1);
//assert(route[sz-1].second.first == q.first);
route[sz-1].second = mp(route[sz-1].second.second, q.second);
} else {
route.push_back(mp(c, q));
}
}
if (c == wc) break;
go_up(c, p);
}
if (uv == 1)
reverse(route.begin() + sz, route.end());
}
}
private:
void measure (const vvi &g, int root, vi & out_subtreesizes) const {
out_subtreesizes.assign(size(g), -1);
stack<int> st;
st.push(root);
while(!st.empty()) {
auto n = st.top(); st.pop();
if (out_subtreesizes[n] == -2) {
int sum = 1;
for (const auto & v : g[n])
if (out_subtreesizes[v] != -2)
sum += out_subtreesizes[v];
out_subtreesizes[n] = sum;
} else {
st.push(n);
for (const auto & v : g[n])
if (out_subtreesizes[v] != -2)
st.push(v);
out_subtreesizes[n] = -2;
}
}
}
};
struct SegmentTree {
int N, size;
vi seg;
void init (const vi::iterator begin, const vi::iterator end) {
N = end - begin;
size = 2 * N;
while (bitcount(size) != 1) ++size;
seg.resize(size, -1);
auto it = begin;
//warn("segment_trees init");
fo (n, size / 2, size / 2 + N - 1) {
seg[n] = *it++;
}
fod (n, size / 2 - 1, 1)
seg[n] = max(seg[2*n], seg[2*n+1]);
}
void update (int pos, int val) {
int n = (size / 2) + pos;
seg[n] = val;
for (n >>= 1; n; n >>= 1)
seg[n] = max (seg[2*n], seg[2*n+1]);
}
int query(int n, int n_l, int n_r, int l, int r) {
if (r < n_l || l > n_r) return -1;
if (l <= n_l && n_r <= r) return seg[n];
int mid = (n_l + n_r) >> 1;
return max(query(2 * n, n_l, mid, l, r), query(2 * n + 1, mid + 1, n_r, l, r));
}
} ;
HeavyPathDecomposition hpd;
vector<SegmentTree> segment_trees;
void test_hpd() {
hpd.build(g, 0);
warn("# chains:", size(hpd.lengths));
HeavyPathDecomposition::chains res;
int max_chains = 0;
re (steps, 100000) {
int u = nextInt(N), v = nextInt(N);
hpd.get_route(u, v, res);
remax(max_chains, size(res));
}
warn("max # of chains:", max_chains);
exit(0);
}
int main() {
random_engine.seed(0);
//solution_timer.startTimer();
Sc.init(stdin);
N = Sc.nextInt(), M = Sc.nextInt();
re (i, N) A[i] = Sc.nextInt();
g.resize(N);
re (i, N - 1) {
int x = Sc.nextInt(), y = Sc.nextInt();
--x, --y;
g[x].pb(y), g[y].pb(x);
}
// Heavy Path
hpd.build(g, 0);
return 0;
segment_trees.resize(hpd.C);
re (c, hpd.C) {
vi vals (hpd.lengths[c]);
re (i, size(vals)) vals[i] = A[*(hpd.nodesBegin(c) + i)];
segment_trees[c].init(all(vals));
}
re (i, M) {
int t = Sc.nextInt(), u = Sc.nextInt(), v = Sc.nextInt();
if (t == 0) {
--u;
int c, pos;
hpd.get(u, c, pos);
segment_trees[c].update(pos, v);
//warn("update:", u, v, "->", c, pos);
// update val
} else {
--u, --v;
//warn("query:", u, v);
HeavyPathDecomposition::chains chains;
hpd.get_route(u, v, chains);
int result = -1;
for (const auto & chain : chains) {
int c = chain.fi, l, r;
tie (l, r) = chain.se;
//warn(":", l, r);
if (l > r) swap(l, r);
r--;
auto max_val = segment_trees[c].query(1, 0, segment_trees[c].N - 1, l, r);
//warn("=>", chain, "l r:", l, r, "answer:", max_val, "interval:", 0, segment_trees[c].N - 1);
remax (result, max_val);
}
// query
printf("%d\n", result);
}
}
return 0;
}