#line 1 "template/template.hpp" #include #if __has_include() #include #endif using namespace std; using int64 = long long; const int64 infll = (1LL << 62) - 1; const int inf = (1 << 30) - 1; struct IoSetup { IoSetup() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(10); cerr << fixed << setprecision(10); } } iosetup; template ostream &operator<<(ostream &os, const pair &p) { os << p.first << " " << p.second; return os; } template istream &operator>>(istream &is, pair &p) { is >> p.first >> p.second; return is; } template ostream &operator<<(ostream &os, const vector &v) { for (size_t i = 0; i < v.size(); i++) { os << v[i] << (i + 1 != v.size() ? " " : ""); } return os; } template istream &operator>>(istream &is, vector &v) { for (T &in: v) is >> in; return is; } template bool chmax(T1 &a, T2 b) { return a < b && (a = b, true); } template bool chmin(T1 &a, T2 b) { return a > b && (a = b, true); } template vector make_v(size_t a) { return vector(a); } template auto make_v(size_t a, Ts... ts) { return vector(ts...))>(a, make_v(ts...)); } template enable_if_t == 0> fill_v(T &t, const V &v) { t = v; } template enable_if_t != 0> fill_v(T &t, const V &v) { for (auto &e: t) fill_v(e, v); } template struct FixPoint : F { explicit FixPoint(F &&f) : F(std::forward(f)) { } template decltype(auto) operator()(Args &&... args) const { return F::operator()(*this, std::forward(args)...); } }; template decltype(auto) MFP(F &&f) { return FixPoint{std::forward(f)}; } #line 2 "graph/tree/centroid-decomposition.hpp" #include #line 2 "graph/graph-template.hpp" #include #include #line 6 "graph/graph-template.hpp" template struct Edge { int from, to; T cost; int idx; Edge() = default; Edge(int from, int to, T cost = 1, int idx = -1) : from(from), to(to), cost(cost), idx(idx) { } operator int() const { return to; } }; template struct Graph { std::vector > > g; int es; Graph() = default; explicit Graph(int n) : g(n), es(0) { } std::size_t size() const { return g.size(); } void add_directed_edge(int from, int to, T cost = 1) { g[from].emplace_back(from, to, cost, es++); } void add_edge(int from, int to, T cost = 1) { g[from].emplace_back(from, to, cost, es); g[to].emplace_back(to, from, cost, es++); } void read(int M, int padding = -1, bool weighted = false, bool directed = false) { for (int i = 0; i < M; i++) { int a, b; std::cin >> a >> b; a += padding; b += padding; T c = T(1); if (weighted) std::cin >> c; if (directed) add_directed_edge(a, b, c); else add_edge(a, b, c); } } inline std::vector > &operator[](const int &k) { return g[k]; } inline const std::vector > &operator[](const int &k) const { return g[k]; } }; template using Edges = std::vector >; #line 6 "graph/tree/centroid-decomposition.hpp" /** * @brief Centroid-Decomposition(重心分解) */ template struct CentroidDecomposition : Graph { public: using Graph::Graph; using Graph::g; Graph tree; int build(int t = 0) { sub.assign(g.size(), 0); v.assign(g.size(), 0); tree = Graph(g.size()); return build_dfs(0); } explicit CentroidDecomposition(const Graph &g) : Graph(g) { } private: std::vector sub; std::vector v; inline int build_dfs(int idx, int par) { sub[idx] = 1; for (auto &to: g[idx]) { if (to == par || v[to]) continue; sub[idx] += build_dfs(to, idx); } return sub[idx]; } inline int search_centroid(int idx, int par, const int mid) { for (auto &to: g[idx]) { if (to == par || v[to]) continue; if (sub[to] > mid) return search_centroid(to, idx, mid); } return idx; } inline int build_dfs(int idx) { int centroid = search_centroid(idx, -1, build_dfs(idx, -1) / 2); v[centroid] = true; for (auto &to: g[centroid]) { if (!v[to]) tree.add_directed_edge(centroid, build_dfs(to)); } v[centroid] = false; return centroid; } }; #line 2 "structure/convex-hull-trick/dynamic-li-chao-tree.hpp" #include #include /** * @brief Dynamic-Li-Chao-Tree * */ template struct DynamicLiChaoTree { struct Line { T a, b; Line(T a, T b) : a(a), b(b) { } inline T get(T x) const { return a * x + b; } }; struct Node { Line x; Node *l, *r; Node(const Line &x) : x{x}, l{nullptr}, r{nullptr} { } }; Node *root; DynamicLiChaoTree() : root{nullptr} { } Node *add_line(Node *t, Line &x, const T &l, const T &r, const T &x_l, const T &x_r) { if (!t) return new Node(x); T t_l = t->x.get(l), t_r = t->x.get(r); if (t_l <= x_l && t_r <= x_r) { return t; } else if (t_l >= x_l && t_r >= x_r) { t->x = x; return t; } else { T m = (l + r) / 2; if (m == r) --m; T t_m = t->x.get(m), x_m = x.get(m); if (t_m > x_m) { std::swap(t->x, x); if (x_l >= t_l) t->l = add_line(t->l, x, l, m, t_l, t_m); else t->r = add_line(t->r, x, m + 1, r, t_m + x.a, t_r); } else { if (t_l >= x_l) t->l = add_line(t->l, x, l, m, x_l, x_m); else t->r = add_line(t->r, x, m + 1, r, x_m + x.a, x_r); } return t; } } void add_line(const T &a, const T &b) { Line x(a, b); root = add_line(root, x, x_low, x_high, x.get(x_low), x.get(x_high)); } Node *add_segment(Node *t, Line &x, const T &a, const T &b, const T &l, const T &r, const T &x_l, const T &x_r) { if (r < a || b < l) return t; if (a <= l && r <= b) { Line y{x}; return add_line(t, y, l, r, x_l, x_r); } if (t) { T t_l = t->x.get(l), t_r = t->x.get(r); if (t_l <= x_l && t_r <= x_r) return t; } else { t = new Node(Line(0, id)); } T m = (l + r) / 2; if (m == r) --m; T x_m = x.get(m); t->l = add_segment(t->l, x, a, b, l, m, x_l, x_m); t->r = add_segment(t->r, x, a, b, m + 1, r, x_m + x.a, x_r); return t; } void add_segment(const T &l, const T &r, const T &a, const T &b) { Line x(a, b); root = add_segment(root, x, l, r - 1, x_low, x_high, x.get(x_low), x.get(x_high)); } T query(const Node *t, const T &l, const T &r, const T &x) const { if (!t) return id; if (l == r) return t->x.get(x); T m = (l + r) / 2; if (m == r) --m; if (x <= m) return std::min(t->x.get(x), query(t->l, l, m, x)); else return std::min(t->x.get(x), query(t->r, m + 1, r, x)); } T query(const T &x) const { return query(root, x_low, x_high, x); } }; int main() { // 直感的には重心分解して各頂点について最大値を求めれば良い int N; cin >> N; vector A(N); cin >> A; CentroidDecomposition g(N); g.read(N - 1); auto root = g.build(); vector used(N); struct Node { int idx, par, dep; int64 sum; }; auto ans = A; auto rec = MFP([&](auto rec, int centroid) -> void { used[centroid] = true; vector > chs; for (auto sub: g[centroid]) { if (used[sub]) continue; chs.emplace_back(); auto dfs = MFP([&](auto dfs, int v, int par, int dep, int64 sum) -> void { chs.back().emplace_back( v, par, dep, sum - 1ll * dep * (dep + 1) / 2); for (auto to: g[v]) { if (to == par or used[to]) continue; dfs(to, v, dep + 1, sum + A[to]); } }); dfs(sub, centroid, 1, A[centroid] + A[sub]); } vector best(N, -infll); auto baka = [&] { DynamicLiChaoTree cht; cht.add_line(0, -A[centroid]); for (const auto& sub : chs) { for (auto& [v, par, dep, sum]: sub) { chmax(best[v], sum - A[centroid] - cht.query(dep)); } for (auto& [v, par, dep, sum]: sub) { cht.add_line(dep, -sum); } } }; baka(); ranges::reverse(chs); baka(); for (auto &vs: chs) { ranges::reverse(vs); for (auto& [v, par, dep, sum]: vs) { chmax(ans[v], best[v]); ans[centroid] = max(ans[centroid], best[v]); if (par != centroid) { chmax(best[par], best[v]); } } } for (auto to: g.tree[centroid]) { rec(to); } used[centroid] = false; }); rec(root); cout << *ranges::min_element(ans) << endl; }