#include  <bits/stdc++.h>
#define rep(i,n) for(int (i)=0;(i)<(n);(i)++)
using namespace std;

using ll = long long;
using state = tuple<ll, ll, ll, ll>;
const ll inf = 1LL << 50;

inline state vmerge(state l, state r) {
	state ret;
	get<0>(ret) = max({get<0>(l), get<0>(r), get<3>(l) + get<2>(r)});
	get<1>(ret) = get<1>(l) + get<1>(r);
	get<2>(ret) = max(get<2>(l), get<1>(l) + get<2>(r));
	get<3>(ret) = max(get<3>(r), get<1>(r) + get<3>(l));
	return ret;
}

template <int depth>  struct Segment_tree {
	const static int h = depth;
	const static int n = 1 << h;

	using T = ll;
	state data[2 * n];
	T lazy[2 * n];

	const state data_out = make_tuple(-inf, 0LL, -inf, -inf);
	const T lazy_out = (1LL << 31) - 1;


	void init() {
		fill_n(data, 2 * n, data_out);
		fill_n(lazy, 2 * n, lazy_out);
	}

	// lazy_evaluation
	void apply(T v, int p, int l, int r) {
		T ans = max(v, v * (r - l)), all = v * (r - l);
		T left = ans, right = ans;
		data[p] = make_tuple(ans, all, left, right);
		lazy[p] = v;
	}

	void push(int p, int l, int r) {
		const int m = (l + r) / 2;
		if (lazy[p] == lazy_out) return;
		apply(lazy[p], 2 * p + 1, l, m);
		apply(lazy[p], 2 * p + 2, m, r);
		lazy[p] = lazy_out;
	}

	void range_update(int a, int b, T x, int k = 0, int l = 0, int r = n) {
		if (r <= a or b <= l) return;
		if (a <= l and r <= b) return apply(x, k, l, r);
		push(k, l, r);
		const int m = (l + r) / 2;
		range_update(a, b, x, k * 2 + 1, l, m);
		range_update(a, b, x, k * 2 + 2, m, r);
		data[k] = vmerge(data[k * 2 + 1], data[k * 2 + 2]);
	}

	state range_query(int a, int b, int k = 0, int l = 0, int r = n) {
		if (r <= a or b <= l) return data_out;
		if (a <= l and r <= b) return data[k];
		push(k, l, r);
		const int m = (l + r) / 2;
		state vl = range_query(a, b, k * 2 + 1, l, m);
		state vr = range_query(a, b, k * 2 + 2, m, r);
		return vmerge(vl, vr);
	}
};

Segment_tree<18> seg;

using edge = struct {int to;};
using G = vector<vector<edge>>;

template <int V> struct HLD {
	int par[V], depth[V], heavy[V];
	int head[V], vid[V], inv[V];

	int dfs(const G& graph, int v, int p) {
		int sz = 1, smax = 0;
		for (auto &e : graph[v]) {
			if (e.to == p) continue;
			par[e.to] = v, depth[e.to] = depth[v] + 1;

			int sub_sz = dfs(graph, e.to, v);
			sz += sub_sz;

			if (smax < sub_sz) {
				smax = sub_sz, heavy[v] = e.to;
			}
		}
		return sz;
	}

	void init(const G& graph) {
		const int n = graph.size();
		fill_n(heavy, n, -1);
		dfs(graph, 0, -1);

		/*
		int id = 0;
		rep(h, n) {
			if (par[h] != -1 and heavy[par[h]] == h) continue;
			for (int v = h; v != -1; v = heavy[v]) {
				inv[id] = v, vid[v] = id++, head[v] = h;
			}
		}
		*/

		int id = 0;
		queue<int> q({ 0 });
		while (!q.empty()) {
			int h = q.front(); q.pop();
			for (int i = h; i != -1; i = heavy[i]) {
				vid[i] = id++;
				inv[vid[i]] = i;
				head[i] = h;
				for (auto &e: graph[i]) if (e.to != par[i] && e.to != heavy[i]) q.push(e.to);
			}
		}
	}

	// ???????±???§??? for_each ????????? f???3??????????????°?????????????????????0??????????????????1???????????????
	void for_each_vertex_directed(int u, int v, auto &f) {
		if (vid[u] > vid[v]) {
			f(max(vid[head[u]], vid[v]), vid[u] + 1, 1);
			if (head[u] != head[v]) for_each_vertex_directed(par[head[u]], v, f);
		} else {
			f(max(vid[head[v]], vid[u]), vid[v] + 1, 0);
			if (head[u] != head[v]) for_each_vertex_directed(u, par[head[v]], f);
		}
	}
};

const int nmax = 200010;
HLD<nmax> hld;

int n, q;
int a[nmax], b[nmax], w[nmax], k;

void run(void) {
	cin >> n >> q;
	rep(i, n) cin >> w[i];

	G tree(n);
	rep(i, n - 1) {
		cin >> a[i] >> b[i];
		a[i]--, b[i]--;
		tree[a[i]].push_back({b[i]});
		tree[b[i]].push_back({a[i]});
	}

	hld.init(tree);

	rep(i, n) {
		auto update = [&](int u, int v, int d) {
			//cerr << "i = " << i << endl;
			//cerr << "(u,v) = " << u << " " << v << endl;
			seg.range_update(u, v, w[i]);
			return;
		};
		hld.for_each_vertex_directed(i, i, update);
	}

	rep(loop, q) {
		int t, a, b; ll c;
		cin >> t >> a >> b >> c;
		a--, b--;

		if (t == 1) {
			auto update = [&](int u, int v, int d) {
				//cerr << "Update: (u,v,d) = " << u << " " << v << " " << d << endl;
				seg.range_update(u, v , c);
			};
			hld.for_each_vertex_directed(a, b, update);
		} else {
			state lft = make_tuple(-inf, 0LL, -inf, -inf);
			state rht = make_tuple(-inf, 0LL, -inf, -inf);

			auto query = [&](int u, int v, int d) {
				//cerr << "Query: (u,v,d) = " << u << " " << v << " " << d << endl;
				if (d == 0)
					lft = vmerge(seg.range_query(u, v), lft);
				else
					rht = vmerge(seg.range_query(u, v), rht);
			};
			hld.for_each_vertex_directed(a, b, query);

			swap(get<2>(lft), get<3>(lft));
			cout << get<0>(vmerge(lft, rht)) << endl;
		}
	}
	return;
}

long long esp_org, esp_new; //??????????????????
int main() {
	run();

	/*
	const int size = 1 << 27;
	void *p = malloc(size);
	esp_new = (long long)p + size - 1;
	__asm__("mov %%rsp, %0" : "=r"(esp_org) : : );
	__asm__("mov %0, %%rsp" : : "r"(esp_new) : );
	run();
	__asm__("mov %0, %%rsp" : : "r"(esp_org) : );
	free(p);
	*/

	return 0;
}

Measured memory_bytes: 48272