#define REP(a,b) for(int a=0; a<(b); ++a)
#define REP2(a,b) for(int a=1; a<=(b); ++a)
#define FWD(a,b,c) for(int a=(b); a<(c); ++a)
#define BCK(a,b,c) for(int a=(b)-1; a>=(c); --a)
#define BCK2(a,b,c) for(int a=(b); a>(c); --a)
#define FWDS(a,b,c,d) for(int a=(b); a<(c); a+=d)
#define ALL(a) (a).begin(), (a).end()
#define SIZE(a) ((int)(a).size())
#define VAR(x) #x ": " << x << " "
#define FILL(x,y) memset(x,y,sizeof(x))
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#define FAST ios_base::sync_with_stdio(0);cin.tie(0);
#define x first
#define y second
#define st first
#define nd second
#define mp make_pair
#define pb push_back
#define l(n) (n<<1)
#define r(n) ((n<<1)+1)
#define f(n) (n>>1)
#define lsb(a) (a&-a)
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>
using namespace std;
#ifndef ONLINE_JUDGE
#include<fstream>
ifstream cin("heavypath.in");
ofstream cout("heavypath.out");
#else
#include<iostream>
#endif
const int NMAX = 100015;
const int dx[] = {0, 0, -1, 1}; //1,1,-1,1};
const int dy[] = {-1, 1, 0, 0}; //1,-1,1,-1};
typedef long long LL;
typedef pair<int, int> PII;
typedef vector<int> VI;
int N,M,A[NMAX];
VI G[NMAX], P[NMAX]; // Graph and paths
int path[NMAX]; // Index of the path on which node n stands;
int conn[NMAX]; // The connection node of the path to the above path
int conn_lvl[NMAX]; // The connection node lvl; a.k.a. the starting high level;
int length[NMAX]; // The length of the path
int position[NMAX]; // The starting position of the path in the ST.
int K;
int segment[4*NMAX];
bool viz[NMAX]; // use for DFS
int w[NMAX]; // weight
int lvl[NMAX]; // depth of the node starting from the root
int asd=0;
void dfs(int n)
{
//cout<<"n = "<<n<<" ";
viz[n]=true;
w[n]=1;
int son=0;
bool end=true;
REP(i,G[n].size())
{
int curr=G[n][i];
if(!viz[curr])
{
end=false;
lvl[curr]=lvl[n]+1;
dfs(curr);
w[n]+=w[curr];
if(!son)
son=curr;
else
if(w[son] < w[curr])
son=curr;
}
}
if(end) // it's a leaf so we create a new path
{
//cout<<"new path \n";
path[n]=++K;
length[path[n]]=1;
P[path[n]].pb(n);
return;
}
// it's not a leaf so adhere this node to the path of the chosen son
path[n]=path[son];
++length[path[n]];
P[path[son]].pb(n);
// connect the paths ending in this node to this node
REP(i, G[n].size())
{
int curr=G[n][i];
if(curr == son || lvl[curr] < lvl[n])
continue;
conn[path[curr]]=n;
//cout<<"conn["<<path[curr]<<"] = "<<n<<" \n";
conn_lvl[path[curr]]=lvl[n];
}
}
void build(int n, int l, int r, int phi, int path)
{
if(l==r)
{
segment[n + phi] = A[P[path][l-1]];
return;
}
int pivot = (l + r)/2;
build(l(n), l, pivot, phi, path);
build(r(n), pivot+1, r, phi, path);
segment[n + phi] = MAX(segment[l(n) + phi], segment[r(n) + phi]);
}
void update(int n, int l, int r, int poz, int val, int phi)
{
if(l==r)
{
segment[n + phi]=val;
//cout<<"updated ! "<<"\n";
return;
}
int pivot = (l+r)>>1;
if(poz <= pivot)
update(l(n), l, pivot, poz, val, phi);
else
update(r(n), pivot+1, r, poz, val, phi);
segment[n + phi] = MAX(segment[l(n) + phi], segment[r(n) + phi]);
//cout<<" go back "<<"\n";
}
int query(int n, int l, int r, int x, int y, int phi)
{
if(x <= l && r <= y)
return segment[n + phi];
int med = (l + r) / 2, rez = 0;
if(x <= med)
rez = max(rez, query(n * 2, l, med, x, y, phi));
if(med < y)
rez = max(rez, query(n * 2 + 1, med + 1, r, x, y, phi));
return rez;
}
int main(){
FAST;
cin>>N>>M;
REP2(i,N)
cin>>A[i];
REP(i,N-1)
{
int x,y;
cin>>x>>y;
//cout<<x<<" "<<y<<" \n";
G[x].pb(y); G[y].pb(x);
}
//cout<<"end \n";
// DFS AND BUILD PATHS
lvl[1]=1;
dfs(1); // build the paths;
//cout<<"k = "<<K<<"\n";
cout<<"K IS "<<K<<" \n";
REP2(i, K)
{
reverse(P[i].begin(), P[i].end()); // reverse the paths so its starting with the lowest level node.
if(i>1)
position[i] = position[i-1] + 4*length[i-1]; // why is 4 is intelligible to me....
build(1, 1, length[i], position[i], i);
//cout<<"new \n";
}
// ----
int t,x,y,sol=0;
REP(i,M)
{
cin>>t>>x>>y;
//cout<<" doo for "<<t<<" "<<x<<" "<<y<<" \n";
if(t==0)
{
update(1, 1, length[path[x]], lvl[x] - conn_lvl[path[x]], y, position[path[x]]);
}
else
{
sol=0;
while(1)
{
asd++;
if(path[x] == path[y])
{
//cout<<"path["<<x<<"] = path["<<y<<"]"<<"\n";
if(lvl[x] > lvl[y])
swap(x,y);
sol = MAX(sol, query(1, 1, length[path[x]], lvl[x]-conn_lvl[path[x]], lvl[y]-conn_lvl[path[x]], position[path[x]]));
break;
}
if(conn_lvl[path[x]] < conn_lvl[path[y]])
swap(x,y);
sol = MAX(sol, query(1, 1, length[path[x]], 1, lvl[x] - conn_lvl[path[x]], position[path[x]]));
//cout<<conn[path[x]]<<" ";
x=conn[path[x]];
}
//cout<<"sol = ";
cout<<sol<<" "<<asd<<" "<<"\n";
}
}
cout<<" K is "<<K<<" ";
return 0;
}
/*
//Heavy Path Decomposition, O(N * log N * log N)
#include <cstdio>
#include <algorithm>
#include <vector>
#include <utility>
using namespace std;
#define MAXN 100010
int N, M, nL;
int v[MAXN], fol[MAXN], niv[MAXN], w[MAXN], l[MAXN];
int aint[4*MAXN];
int lTata[MAXN], lNiv[MAXN], lDim[MAXN], lPoz[MAXN];
vector<int> G[MAXN], P[MAXN];
pair<int, pair<int, int> > op[MAXN];
int asd=0;
void citire()
{
scanf("%d%d", &N, &M);
for(int i = 1; i <= N; ++i)
scanf("%d", &v[i]);
int a, b;
for(int i = 1; i < N; ++i)
{
scanf("%d%d", &a, &b);
G[a].push_back(b);
G[b].push_back(a);
}
int t, x, y;
for(int i = 1; i <= M; ++i)
{
scanf("%d%d%d", &t, &x, &y);
op[i] = make_pair(t, make_pair(x, y));
}
}
void df(int nod)
{
fol[nod] = 1;
w[nod] = 1;
int hN = -1, frunza = 1;
for(vector<int> :: iterator it = G[nod].begin(); it != G[nod].end(); ++it)
{
if(fol[*it])
continue;
frunza = 0;
niv[*it] = niv[nod] + 1;
df(*it);
w[nod] += w[*it];
if(hN == -1)
hN = *it;
else
if(w[hN] < w[*it])
hN = *it;
}
if(frunza)
{
l[nod] = ++nL;
lDim[nL]=1;
P[nL].push_back(nod);
return;
}
l[nod] = l[hN];
++lDim[l[nod]];
P[l[nod]].push_back(nod);
for(vector<int> :: iterator it = G[nod].begin(); it != G[nod].end(); ++it)
{
if((*it) == hN || niv[*it] < niv[nod])
continue;
lTata[l[*it]] = nod;
lNiv[l[*it]] = niv[nod];
}
}
void build(int nod, int left, int right, int decalaj, int lant)
{
if(left == right)
{
aint[nod + decalaj] = v[ P[lant][left - 1] ];
return;
}
int med = (left + right) / 2;
build(nod * 2, left, med, decalaj, lant);
build(nod * 2 + 1, med+1, right, decalaj, lant);
aint[nod + decalaj] = max(aint[nod * 2 + decalaj], aint[nod * 2 + 1 + decalaj]);
}
void make_paths()
{
niv[1] = 1;
df(1);
for(int i = 1; i <= nL; ++i)
{
reverse(P[i].begin(), P[i].end());
if(i > 1)
lPoz[i] = lPoz[i-1] + lDim[i-1] * 4;
build(1, 1, lDim[i], lPoz[i], i);
}
}
void update(int nod, int left, int right, int poz, int val, int decalaj)
{
if(left == right)
{
aint[nod + decalaj] = val;
return;
}
int med = (left + right) / 2;
if(poz<=med)
update(nod * 2, left, med, poz, val, decalaj);
else
update(nod * 2 + 1, med+1, right, poz, val, decalaj);
aint[nod + decalaj] = max(aint[nod * 2 + decalaj], aint[nod * 2 + 1 + decalaj]);
}
int query(int nod, int left, int right, int qleft, int qright, int decalaj)
{
asd++;
if(qleft <= left && right <= qright)
return aint[nod + decalaj];
int med = (left + right) / 2, rez = 0;
if(qleft <= med)
rez = max(rez, query(nod * 2, left, med, qleft, qright, decalaj) );
if(med < qright)
rez = max(rez, query(nod * 2 + 1, med + 1, right, qleft, qright, decalaj) );
return rez;
}
void solve()
{
int t, x, y, sol = 0;
for(int i = 1; i <= M; ++i)
{
t = op[i].first; x = op[i].second.first, y = op[i].second.second;
if(t==0)
update(1, 1, lDim[l[x]], niv[x] - lNiv[l[x]], y, lPoz[l[x]]);
else
{
sol = 0;
while(1)
{
asd++;
if(l[x] == l[y])
{
if(niv[x] > niv[y])
swap(x, y);
sol = max(sol, query(1, 1, lDim[l[x]], niv[x] - lNiv[l[x]], niv[y] - lNiv[l[x]], lPoz[l[x]]) );
break;
}
if(lNiv[l[x]] < lNiv[l[y]])
swap(x, y);
sol = max(sol, query(1, 1, lDim[l[x]], 1, niv[x] - lNiv[l[x]], lPoz[l[x]]) );
x = lTata[l[x]];
}
printf("%d %d\n", sol, asd);
}
}
}
int main()
{
freopen("heavypath.in", "r", stdin);
freopen("heavypath.out", "w", stdout);
citire();
make_paths();
solve();
return 0;
}
*/