// code template is in https://github.com/drken1215/algorithm/blob/master/template_minimum.cpp #pragma GCC optimize("Ofast") #pragma GCC optimize("unroll-loops") #include using namespace std; //------------------------------// // Utility //------------------------------// using ll = long long; using i128 = __int128_t; using u128 = __uint128_t; using pint = pair; using pll = pair; using tll = array; using fll = array; using vint = vector; using vll = vector; using dint = deque; using dll = deque; using vvint = vector>; using vvll = vector>; using vpll = vector>; template using min_priority_queue = priority_queue, greater>; template inline bool chmax(S &a, T b) { return (a < b ? a = b, 1 : 0); } template inline bool chmin(S &a, T b) { return (a > b ? a = b, 1 : 0); } template inline auto maxll(S a, T b) { return max(ll(a), ll(b)); } template inline auto minll(S a, T b) { return min(ll(a), ll(b)); } template auto max(const T &a) { return *max_element(a.begin(), a.end()); } template auto min(const T &a) { return *min_element(a.begin(), a.end()); } template auto argmax(const T &a) { return max_element(a.begin(), a.end()) - a.begin(); } template auto argmin(const T &a) { return min_element(a.begin(), a.end()) - a.begin(); } template auto accum(const vector &a) { return accumulate(a.begin(), a.end(), T()); } template auto accum(const deque &a) { return accumulate(a.begin(), a.end(), T()); } #define REP(i, a) for (long long i = 0; i < (long long)(a); i++) #define REP2(i, a, b) for (long long i = a; i < (long long)(b); i++) #define RREP(i, a) for (long long i = (a)-1; i >= (long long)(0); --i) #define RREP2(i, a, b) for (long long i = (b)-1; i >= (long long)(a); --i) #define EB emplace_back #define PF push_front #define PB push_back #define MP make_pair #define FI first #define SE second #define ALL(x) x.begin(), x.end() #define COUT(x) cout << #x << " = " << (x) << " (L" << __LINE__ << ")" << endl // input template istream& operator >> (istream &is, vector &P) { for (int i = 0; i < (int)P.size(); ++i) cin >> P[i]; return is; } template istream& operator >> (istream &is, deque &P) { for (int i = 0; i < (int)P.size(); ++i) cin >> P[i]; return is; } template istream& operator >> (istream &is, vector> &P) { for (int i = 0; i < (int)P.size(); ++i) cin >> P[i]; return is; } // output template ostream& operator << (ostream &s, const pair &P) { return s << '<' << P.first << ", " << P.second << '>'; } template ostream& operator << (ostream &s, const array &P) { return s << '<' << P[0] << "," << P[1] << '>'; } template ostream& operator << (ostream &s, const array &P) { return s << '<' << P[0] << "," << P[1] << "," << P[2] << '>'; } template ostream& operator << (ostream &s, const array &P) { return s << '<' << P[0] << "," << P[1] << "," << P[2] << "," << P[3] << '>'; } template ostream& operator << (ostream &s, const vector &P) { for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; } template ostream& operator << (ostream &s, const deque &P) { for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; } template ostream& operator << (ostream &s, const vector> &P) { for (int i = 0; i < P.size(); ++i) { s << endl << P[i]; } return s << endl; } template ostream& operator << (ostream &s, const set &P) { for (auto it : P) { s << "<" << it << "> "; } return s; } template ostream& operator << (ostream &s, const multiset &P) { for (auto it : P) { s << "<" << it << "> "; } return s; } template ostream& operator << (ostream &s, const unordered_set &P) { for (auto it : P) { s << "<" << it << "> "; } return s; } template ostream& operator << (ostream &s, const map &P) { for (auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s; } template ostream& operator << (ostream &s, const unordered_map &P) { for (auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s; } void yes(bool a) { cout << (a ? "yes" : "no") << endl; } void YES(bool a) { cout << (a ? "YES" : "NO") << endl; } void Yes(bool a) { cout << (a ? "Yes" : "No") << endl; } const vector DX = {1, 0, -1, 0, 1, -1, 1, -1}; const vector DY = {0, 1, 0, -1, 1, -1, -1, 1}; //------------------------------// // Solver //------------------------------// // modint template struct Fp { // inner value unsigned int val; // constructor constexpr Fp() : val(0) { } template constexpr Fp(T v) { long long tmp = (long long)(v % (long long)(get_umod())); if (tmp < 0) tmp += get_umod(); val = (unsigned int)(tmp); } template constexpr Fp(T v) { val = (unsigned int)(v % get_umod()); } constexpr long long get() const { return val; } constexpr static int get_mod() { return MOD; } constexpr static unsigned int get_umod() { return MOD; } // arithmetic operators constexpr Fp operator + () const { return Fp(*this); } constexpr Fp operator - () const { return Fp() - Fp(*this); } constexpr Fp operator + (const Fp &r) const { return Fp(*this) += r; } constexpr Fp operator - (const Fp &r) const { return Fp(*this) -= r; } constexpr Fp operator * (const Fp &r) const { return Fp(*this) *= r; } constexpr Fp operator / (const Fp &r) const { return Fp(*this) /= r; } constexpr Fp& operator += (const Fp &r) { val += r.val; if (val >= get_umod()) val -= get_umod(); return *this; } constexpr Fp& operator -= (const Fp &r) { val -= r.val; if (val >= get_umod()) val += get_umod(); return *this; } constexpr Fp& operator *= (const Fp &r) { unsigned long long tmp = val; tmp *= r.val; val = (unsigned int)(tmp % get_umod()); return *this; } constexpr Fp& operator /= (const Fp &r) { return *this = *this * r.inv(); } constexpr Fp pow(long long n) const { assert(n >= 0); Fp res(1), mul(*this); while (n) { if (n & 1) res *= mul; mul *= mul; n >>= 1; } return res; } constexpr Fp inv() const { assert(val); if (PRIME) { return pow(get_umod() - 2); } else { assert(gcd(val, get_umod()) == 1); long long m = get_umod(), a = val, b = m, u = 1, v = 0; while (b > 0) { auto t = a / b; a -= t * b, swap(a, b); u -= t * v, swap(u, v); } return Fp(u); } } // other operators constexpr bool operator == (const Fp &r) const { return this->val == r.val; } constexpr bool operator != (const Fp &r) const { return this->val != r.val; } constexpr bool operator < (const Fp &r) const { return this->val < r.val; } constexpr bool operator > (const Fp &r) const { return this->val > r.val; } constexpr bool operator <= (const Fp &r) const { return this->val <= r.val; } constexpr bool operator >= (const Fp &r) const { return this->val >= r.val; } constexpr Fp& operator ++ () { ++val; if (val == get_umod()) val = 0; return *this; } constexpr Fp& operator -- () { if (val == 0) val = get_umod(); --val; return *this; } constexpr Fp operator ++ (int) { Fp res = *this; ++*this; return res; } constexpr Fp operator -- (int) { Fp res = *this; --*this; return res; } friend constexpr istream& operator >> (istream &is, Fp &x) { long long tmp = 1; is >> tmp; tmp = tmp % (long long)(get_umod()); if (tmp < 0) tmp += get_umod(); x.val = (unsigned int)(tmp); return is; } friend constexpr ostream& operator << (ostream &os, const Fp &x) { return os << x.val; } friend constexpr Fp pow(const Fp &r, long long n) { return r.pow(n); } friend constexpr Fp inv(const Fp &r) { return r.inv(); } }; // Edge Class template struct Edge { int from, to; T val; Edge() : from(-1), to(-1) { } Edge(int f, int t, T v = 1) : from(f), to(t), val(v) {} Edge(const Edge&) = default; Edge& operator = (const Edge&) = default; friend ostream& operator << (ostream& s, const Edge& e) { return s << e.from << "->" << e.to << "(" << e.val << ")"; } }; // graph class template struct Graph { int V; bool record_reversed_edges = false, record_edge_index = false; vector>> list; vector>> reversed_list; vector> id; // id[v][w] := the index of node w in G[v] // constructors Graph(int n = 0, bool rre = false, bool rei = false) { init(n, rre, rei); } void init(int n = 0, bool rre = false, bool rei = false) { V = n, record_reversed_edges = rre, record_edge_index = rei; list.assign(n, vector>()); if (record_reversed_edges) reversed_list.assign(n, vector>()); if (record_edge_index) id.assign(n, unordered_map()); } Graph(const Graph&) = default; Graph& operator = (const Graph&) = default; // getters vector> &operator [] (int i) { return list[i]; } const vector> &operator [] (int i) const { return list[i]; } constexpr size_t size() const { return list.size(); } constexpr void clear() { V = 0; list.clear(); } constexpr void resize(int n) { V = n; list.resize(n); } const vector> &get_rev_edges(int i) const { assert(record_reversed_edges); return reversed_list[i]; } Edge &get_edge(int u, int v) { assert(record_edge_index); assert(u >= 0 && u < list.size() && v >= 0 && v < list.size()); assert(id[u].count(v) && id[u][v] >= 0 && id[u][v] < list[u].size()); return list[u][id[u][v]]; } const Edge &get_edge(int u, int v) const { assert(record_edge_index); assert(u >= 0 && u < list.size() && v >= 0 && v < list.size()); assert(id[u].count(v) && id[u].at(v) >= 0 && id[u].at(v) < list[u].size()); return list[u][id[u].at(v)]; } // add edge void add_edge(int from, int to, T val = 1) { assert(0 <= from && from < list.size() && 0 <= to && to < list.size()); if (record_edge_index) id[from][to] = (int)list[from].size(); list[from].push_back(Edge(from, to, val)); if (record_reversed_edges) reversed_list[to].push_back(Edge(to, from, val)); } void add_bidirected_edge(int from, int to, T val = 1) { assert(0 <= from && from < list.size() && 0 <= to && to < list.size()); if (record_edge_index) id[from][to] = (int)list[from].size(); list[from].push_back(Edge(from, to, val)); if (record_reversed_edges) reversed_list[to].push_back(Edge(to, from, val)); if (from != to) { if (record_edge_index) id[to][from] = (int)list[to].size(); list[to].push_back(Edge(to, from, val)); if (record_reversed_edges) reversed_list[from].push_back(Edge(from, to, val)); } } // input (only tree-case) friend istream& operator >> (istream &is, Graph &G) { for (int i = 0; i < G.V - 1; i++) { int u, v; is >> u >> v, u--, v--; G.add_bidirected_edge(u, v); } return is; } // output friend ostream &operator << (ostream &os, const Graph &G) { os << endl; for (int i = 0; i < (int)G.size(); ++i) { os << i << " -> "; for (int j = 0; j < (int)G[i].size(); j++) { if (j) os << ", "; os << G[i][j].to << "(" << G[i][j].val << ")"; } os << endl; } return os; } }; // re-rooting /* 通常の木 DP において、頂点 v を根とする部分根付き木に関する再帰関数 rec(v) について、    1. res = IDENTITY    2. 頂点 v の各子頂点 v2 (その辺を e とする) に対して:res = MERGE(res, rec(v2))   (辺重みあり:res = MERGE(res, ADDEDGE(e, rec(v2)))    3. return ADDNODE(v, res)   というような更新を行うものとする。   このような木 DP を全方位木 DP へと拡張する。 */ template struct ReRooting { using MergeFunc = function; using AddNodeFunc = function; // core member Graph G; // input graph Monoid IDENTITY; MergeFunc MERGE; AddNodeFunc ADDNODE; // inner data vector> dp; vector> ids; // constructor ReRooting() {} ReRooting(const Graph &g, const MergeFunc &merge, const AddNodeFunc &addnode, const Monoid &identity) { G = g; IDENTITY = identity; MERGE = merge; ADDNODE = addnode; build(); } // re-looting dp Monoid rec(int v, int p) { Monoid res = IDENTITY; dp[v].assign(G[v].size(), IDENTITY); for (int i = 0; i < G[v].size(); ++i) { int v2 = G[v][i].to; ids[v][v2] = i; if (v2 == p) continue; dp[v][i] = rec(v2, v); res = MERGE(v, res, dp[v][i]); } return ADDNODE(v, res); } void rerec(int v, int p, Monoid pval) { for (int i = 0; i < G[v].size(); ++i) { int v2 = G[v][i].to; if (v2 == p) { dp[v][i] = pval; continue; } } vector left(G[v].size() + 1, IDENTITY); vector right(G[v].size() + 1, IDENTITY); for (int i = 0; i < G[v].size(); ++i) { left[i + 1] = MERGE(v, left[i], dp[v][i]); right[i + 1] = MERGE(v, right[i], dp[v][(int)G[v].size() - i - 1]); } for (int i = 0; i < G[v].size(); ++i) { int v2 = G[v][i].to; if (v2 == p) continue; Monoid pval2 = MERGE(v, left[i], right[(int)G[v].size() - i - 1]); rerec(v2, v, ADDNODE(v, pval2)); } } void build() { dp.assign(G.size(), vector()); ids.assign(G.size(), unordered_map()); int root = 0, nullparent = -1; rec(root, nullparent); rerec(root, nullparent, IDENTITY); } // getter Monoid get(int v) { Monoid res = IDENTITY; for (int i = 0; i < G[v].size(); ++i) { res = MERGE(v, res, dp[v][i]); } return ADDNODE(v, res); } Monoid get(int v, int w) { return dp[v][ids[v][w]]; } // dump friend constexpr ostream& operator << (ostream &os, const ReRooting &rr) { for (int v = 0; v < rr.G.size(); ++v) { for (int i = 0; i < rr.G[v].size(); ++i) { os << rr.G[v][i] << ": " << rr.dp[v][i] << endl; } } return os; } }; int main() { ll N; cin >> N; vll A(N); cin >> A; Graph G(N); REP(i, N - 1) { ll u, v; cin >> u >> v, u--, v--; G.add_bidirected_edge(u, v); } using Node = pll; Node identity(0, 0); auto merge = [&](int v, Node a, Node b) -> Node { pll res = identity; if (a.first < A[v]) if (res.second < a.second) res = a; if (b.first < A[v]) if (res.second < b.second) res = b; return res; }; auto addnode = [&](int v, Node a) -> Node { ll val = A[v], res = A[v] + a.second; return pll(val, res); }; ReRooting rr(G, merge, addnode, identity); //COUT(rr); ll res = 0; REP(v, N) { auto tmp = rr.get(v); chmax(res, tmp.second); } cout << res << endl; }