ICPC2018国内予選 H問題
今年もチームcatsatmatで出場しました。4位で通過出来て嬉しかったです。
- H問題
前原先生の記事
木上のナップサック問題
でだいたいわかりますが、コードを載せておきます。
まず、素直な解法として、dp[v][c] := vの部分木についてコストcで得られるmaxとおくとO(nkk)ですが、このままでは高速化が難しいです。
ポイントは、各頂点vの子たちを、vの必要量の昇順に並べておくと、ある子に潜ることが出来る時その手前の子たちも全部使えて、このことを利用するとdpの配列を持ちつつdfsすることができるという点です。これだけでオーダーが良くなるのは不思議な感じですね。これに加えて各頂点に使える上限があるので面倒になっています(このパートはスライド最小値をやると線形で解けます) よって時間計算量はO(Case * NK)です。100*100*10^5 = 10^9を8秒なので結構実装に気を使わないといけません。
#include <bits/stdc++.h> using namespace std; #define rep(i, n) for (int i = 0; i < (int)n; ++i) typedef vector<int> vi; typedef long long ll; typedef vector<ll> vl; const ll inf = -1e18; const int MN = 110; const int MK = 100010; inline void chmax(ll &x, ll y) { if (x < y) x = y; } int n, k; vi g[MN]; int h[MN], s[MN], lo[MN]; vl dp[MN]; int deq[MK]; void dfs(int v, vl vec) { vl &res = dp[v]; int l = 0, r = 0; for (int i = 0; i <= k; ++i) { if (vec[i] != inf) { ll x = vec[i] - (ll)i * s[v]; while (l+1 <= r) { int id = deq[r-1]; if (vec[id] - id * (ll)s[v] <= x) { --r; } else { break; } } deq[r++] = i; } if (l+1 > r) { res[i] = inf; break; } else { res[i] = vec[deq[l]] - (ll)deq[l] * s[v] + (ll)s[v] * i; if (deq[l] <= i - h[v]) { ++l; } } } for (int to : g[v]) { dfs(to, vec); vl &nx = dp[to]; for (int i = 0; i <= k; ++i) { chmax(vec[i], nx[i]); } l = r = 0; for (int i = 0; i <= k - lo[to]; ++i) { if (nx[i] != inf) { ll x = nx[i] - (ll)i * s[v]; while (l+1 <= r) { int id = deq[r-1]; if (nx[id] - id * (ll)s[v] <= x) { --r; } else { break; } } deq[r++] = i; } if (l+1 > r) { nx[i] = inf; break; } else { ll val = nx[deq[l]] - (ll)deq[l] * s[v] + (ll)s[v] * i; chmax(res[i + lo[to]], val + (ll)s[v] * lo[to]); if (deq[l] <= i - (h[v] - lo[to])) { ++l; } } } } } int main() { while (cin >> n >> k, n) { rep(i, n) dp[i] = vl(k + 1, inf); rep(i, n) g[i].clear(); rep(i, n) cin >> h[i]; rep(i, n) cin >> s[i]; for (int i = 1; i < n; ++i) { int p; cin >> p; --p; g[p].push_back(i); } for (int i = 1; i < n; ++i) { cin >> lo[i]; } rep(i, n) { sort(g[i].begin(), g[i].end(), [&](int a, int b) { return lo[a] < lo[b]; }); } vector<ll> vec(k + 1, inf); vec[0] = 0; dfs(0, vec); cout << *max_element(dp[0].begin(), dp[0].end()) << endl; } return 0; }