結果
| 問題 | No.3660 LIS on Tree |
| コンテスト | |
| ユーザー |
drken1215
|
| 提出日時 | 2026-08-30 16:26:50 |
| 言語 | C++23 (gcc 15.3.0 + boost 1.92.0) |
| 結果 |
AC
|
| 実行時間 | 439 ms / 2,000 ms |
| + 71µs | |
| コード長 | 16,690 bytes |
| 記録 | |
| コンパイル時間 | 3,970 ms |
| コンパイル使用メモリ | 388,604 KB |
| 実行使用メモリ | 134,464 KB |
| 最終ジャッジ日時 | 2026-08-30 16:27:05 |
| 合計ジャッジ時間 | 8,291 ms |
|
ジャッジサーバーID (参考情報) |
judge2_0 / judge1_0 |
(要ログイン)
| ファイルパターン | 結果 |
|---|---|
| sample | AC * 3 |
| other | AC * 20 |
ソースコード
// code template is in https://github.com/drken1215/algorithm/blob/master/template_minimum.cpp
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#include <bits/stdc++.h>
using namespace std;
//------------------------------//
// Utility
//------------------------------//
using ll = long long;
using i128 = __int128_t;
using u128 = __uint128_t;
using pint = pair<int, int>;
using pll = pair<long long, long long>;
using tll = array<long long, 3>;
using fll = array<long long, 4>;
using vint = vector<int>;
using vll = vector<long long>;
using dint = deque<int>;
using dll = deque<long long>;
using vvint = vector<vector<int>>;
using vvll = vector<vector<long long>>;
using vpll = vector<pair<long long, long long>>;
template<class T> using min_priority_queue = priority_queue<T, vector<T>, greater<T>>;
template<class S, class T> inline bool chmax(S &a, T b) { return (a < b ? a = b, 1 : 0); }
template<class S, class T> inline bool chmin(S &a, T b) { return (a > b ? a = b, 1 : 0); }
template<class S, class T> inline auto maxll(S a, T b) { return max(ll(a), ll(b)); }
template<class S, class T> inline auto minll(S a, T b) { return min(ll(a), ll(b)); }
template<class T> auto max(const T &a) { return *max_element(a.begin(), a.end()); }
template<class T> auto min(const T &a) { return *min_element(a.begin(), a.end()); }
template<class T> auto argmax(const T &a) { return max_element(a.begin(), a.end()) - a.begin(); }
template<class T> auto argmin(const T &a) { return min_element(a.begin(), a.end()) - a.begin(); }
template<class T> auto accum(const vector<T> &a) { return accumulate(a.begin(), a.end(), T()); }
template<class T> auto accum(const deque<T> &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<class T> istream& operator >> (istream &is, vector<T> &P)
{ for (int i = 0; i < (int)P.size(); ++i) cin >> P[i]; return is; }
template<class T> istream& operator >> (istream &is, deque<T> &P)
{ for (int i = 0; i < (int)P.size(); ++i) cin >> P[i]; return is; }
template<class T> istream& operator >> (istream &is, vector<vector<T>> &P)
{ for (int i = 0; i < (int)P.size(); ++i) cin >> P[i]; return is; }
// output
template<class S, class T> ostream& operator << (ostream &s, const pair<S, T> &P)
{ return s << '<' << P.first << ", " << P.second << '>'; }
template<class T> ostream& operator << (ostream &s, const array<T, 2> &P)
{ return s << '<' << P[0] << "," << P[1] << '>'; }
template<class T> ostream& operator << (ostream &s, const array<T, 3> &P)
{ return s << '<' << P[0] << "," << P[1] << "," << P[2] << '>'; }
template<class T> ostream& operator << (ostream &s, const array<T, 4> &P)
{ return s << '<' << P[0] << "," << P[1] << "," << P[2] << "," << P[3] << '>'; }
template<class T> ostream& operator << (ostream &s, const vector<T> &P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, const deque<T> &P)
{ for (int i = 0; i < P.size(); ++i) { if (i > 0) { s << " "; } s << P[i]; } return s; }
template<class T> ostream& operator << (ostream &s, const vector<vector<T>> &P)
{ for (int i = 0; i < P.size(); ++i) { s << endl << P[i]; } return s << endl; }
template<class T> ostream& operator << (ostream &s, const set<T> &P)
{ for (auto it : P) { s << "<" << it << "> "; } return s; }
template<class T> ostream& operator << (ostream &s, const multiset<T> &P)
{ for (auto it : P) { s << "<" << it << "> "; } return s; }
template<class T> ostream& operator << (ostream &s, const unordered_set<T> &P)
{ for (auto it : P) { s << "<" << it << "> "; } return s; }
template<class S, class T> ostream& operator << (ostream &s, const map<S, T> &P)
{ for (auto it : P) { s << "<" << it.first << "->" << it.second << "> "; } return s; }
template<class S, class T> ostream& operator << (ostream &s, const unordered_map<S, T> &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<int> DX = {1, 0, -1, 0, 1, -1, 1, -1};
const vector<int> DY = {0, 1, 0, -1, 1, -1, -1, 1};
//------------------------------//
// Solver
//------------------------------//
// modint
template<int MOD = 998244353, bool PRIME = true> struct Fp {
// inner value
unsigned int val;
// constructor
constexpr Fp() : val(0) { }
template<std::signed_integral T> 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<std::unsigned_integral T> 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<class T = long long> 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<class T = long long> struct Graph {
int V;
bool record_reversed_edges = false, record_edge_index = false;
vector<vector<Edge<T>>> list;
vector<vector<Edge<T>>> reversed_list;
vector<unordered_map<int, int>> 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<Edge<T>>());
if (record_reversed_edges) reversed_list.assign(n, vector<Edge<T>>());
if (record_edge_index) id.assign(n, unordered_map<int, int>());
}
Graph(const Graph&) = default;
Graph& operator = (const Graph&) = default;
// getters
vector<Edge<T>> &operator [] (int i) { return list[i]; }
const vector<Edge<T>> &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<Edge<T>> &get_rev_edges(int i) const {
assert(record_reversed_edges);
return reversed_list[i];
}
Edge<T> &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<T> &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<class Monoid, class Weight = long long> struct ReRooting {
using MergeFunc = function<Monoid(int, Monoid, Monoid)>;
using AddNodeFunc = function<Monoid(int, Monoid)>;
// core member
Graph<Weight> G; // input graph
Monoid IDENTITY;
MergeFunc MERGE;
AddNodeFunc ADDNODE;
// inner data
vector<vector<Monoid>> dp;
vector<unordered_map<int,int>> ids;
// constructor
ReRooting() {}
ReRooting(const Graph<Weight> &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<Monoid> left(G[v].size() + 1, IDENTITY);
vector<Monoid> 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<Monoid>());
ids.assign(G.size(), unordered_map<int,int>());
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<ll> 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<Node> 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;
}
drken1215