ARC029 D - 高橋君と木のおもちゃ
二乗の木DPの練習として解いた。
解法
Mステップで使う玉は置かないという選択もできる。木の深い頂点から順番に置いていけば置いた玉が捨てられることはない。したがって、木の頂点にi個置くときはM個中の大きい方i個だけを置くとするのが最善の選択肢になる。i個木に玉を置くとするとき追加される玉の値の和の最大化は実現できた。あとは木から取り除かれるi個の玉の値の和をできるだけ小さくすることができればよい。
これは木DPを用いて実現できる。dp[i][j] = (頂点i以下の部分木からj個取り除いたときの取り除いた玉の値の和の最小) とする。遷移は2つの部分木のマージという形でかける。ret[i]とtmp[i]をマージしてnret[i]にするとする。このときnret[i+j] = min(nret[i+j], ret[i]+tmp[j]) となる。頂点i以下の部分木から取り除く玉の1つ目は必ず頂点iになることに注意。
遷移のマージがO(N^2)で合計O(N^3)っぽいがこれは二乗の木DPと言われるテクで部分木の大きさまでしかみないようにすることでO(N^2)になっている。
#include <bits/stdc++.h> using namespace std; using ll = long long; // #define int ll using PII = pair<ll, ll>; #define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i) #define REP(i, n) FOR(i, 0, n) #define ALL(x) x.begin(), x.end() template<typename T> T &chmin(T &a, const T &b) { return a = min(a, b); } template<typename T> T &chmax(T &a, const T &b) { return a = max(a, b); } template<typename T> bool IN(T a, T b, T x) { return a<=x&&x<b; } template<typename T> T ceil(T a, T b) { return a/b + !!(a%b); } template<typename T> vector<T> make_v(size_t a) { return vector<T>(a); } template<typename T,typename... Ts> auto make_v(size_t a,Ts... ts) { return vector<decltype(make_v<T>(ts...))>(a,make_v<T>(ts...)); } template<typename T,typename V> typename enable_if<is_class<T>::value==0>::type fill_v(T &t, const V &v) { t=v; } template<typename T,typename V> typename enable_if<is_class<T>::value!=0>::type fill_v(T &t, const V &v ) { for(auto &e:t) fill_v(e,v); } template<class S,class T> ostream &operator <<(ostream& out,const pair<S,T>& a){ out<<'('<<a.first<<','<<a.second<<')'; return out; } template<typename T> istream& operator >> (istream& is, vector<T>& vec){ for(T& x: vec) {is >> x;} return is; } template<class T> ostream &operator <<(ostream& out,const vector<T>& a){ out<<'['; for(T i: a) {out<<i<<',';} out<<']'; return out; } int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0}; // DRUL const int INF = 1<<30; const ll LLINF = 1LL<<60; const int MOD = 1000000007; signed main(void) { cin.tie(0); ios::sync_with_stdio(false); ll n; cin >> n; vector<ll> s(n); vector<vector<ll>> g(n); ll sum = 0; REP(i, n) cin >> s[i], sum += s[i]; REP(i, n-1) { ll a, b; cin >> a >> b; a--, b--; g[a].push_back(b); g[b].push_back(a); } ll m; cin >> m; vector<ll> t(m); REP(i, m) cin >> t[i]; using VL = vector<ll>; function<VL(ll,ll)> dfs = [&](ll v, ll p) { VL ret(2, 0); ret[1] = s[v]; for(auto to: g[v]) { if(to == p) continue; auto tmp = dfs(to, v); VL nret(ret.size()+tmp.size()-1, LLINF); nret[0] = 0, nret[1] = s[v]; FOR(i, 1, ret.size()) REP(j, tmp.size()) { if(i+j <= 1) continue; chmin(nret[i+j], ret[i] + tmp[j]); } ret = nret; } return ret; }; auto dp = dfs(0, -1); sort(ALL(t), greater<>()); FOR(i, 1, m) t[i] += t[i-1]; ll ret = sum; REP(i, m) { if(i+1 >= dp.size()) break; chmax(ret, sum - dp[i+1] + t[i]); } cout << ret << endl; return 0; }