#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;
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;
}
else // 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;
if(!conn[path[curr]])
{
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(n+phi>4*NMAX){
//cout<<"over flow \n "; return;
}
//cout<<n<<" "<<l<<" "<<r<<" \n";
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 pivot = (l+r)>>1, rs=0;
if(x <= pivot)
rs = MAX(rs, query(l(n), l, pivot, x, y, phi));
if(pivot < y)
rs = MAX(rs, query(r(n), pivot+1, r, x, y, phi));
return rs;
}
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";
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";
}
// ----
REP(i,M)
{
int t,x,y;
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
{
int sol=0;
while(1)
{
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<<"\n";
}
}
return 0;
}