#line 1 "test.cpp" #define PROBLEM "https://yukicoder.me/problems/no/705" #include using namespace std; #line 4 "/Users/otsuki/Documents/GitHub/algorithm/Optimization/monge_shortest_path_by_cht.hpp" using namespace std; #line 4 "/Users/otsuki/Documents/GitHub/algorithm/Optimization/general_li_chao_tree.hpp" using namespace std; // Dynamic Li Chao Tree for Pairwise Single Crossing Functions /* Func: Pairwise Single Crossing 性を仮定 注意点:i < j なる領域だけでなく、全領域での Pairwise Single Crossing 性が必要!!! x 座標のとりうる値の範囲: [XL, XR) */ template struct DynamicGeneralLiChaoTree { using Func = function; struct IndexedFunc { Func f; int id; VAL eval(X x) const { return f(x); }; }; struct Node { optional func; unique_ptr left, right; }; // inner values X XL, XR; VAL INF = numeric_limits::max() / 2; int next_id = 0; unique_ptr root; // constructors DynamicGeneralLiChaoTree() {} DynamicGeneralLiChaoTree(X xl, X xr, VAL inf = numeric_limits::max() / 2) : XL(xl), XR(xr), INF(inf) { assert(xl < xr); } static bool better(const IndexedFunc &a, const IndexedFunc &b, X x) { return make_pair(a.eval(x), a.id) < make_pair(b.eval(x), b.id); } void clear() { root.reset(); next_id = 0; } // insert(f), in O(log X) void insert(unique_ptr &p, X left, X right, IndexedFunc f) { if (!p) p = make_unique(); if (!p->func) { p->func.emplace(f); return; } X mid = (left + right) / 2; bool left_better = better(f, *p->func, left); bool right_better = better(f, *p->func, right - 1); if (!left_better && !right_better) return; if (left_better && right_better) { p->func.emplace(f); return; } bool mid_better = better(f, *p->func, mid); if (mid_better) { IndexedFunc old = std::move(*p->func); p->func.emplace(f); if (left_better != mid_better) insert(p->left, left, mid, old); else insert(p->right, mid, right, old); } else { if (left_better != mid_better) insert(p->left, left, mid, f); else insert(p->right, mid, right, f); } } int insert(Func f) { int id = next_id++; insert(root, XL, XR, IndexedFunc{std::move(f), id}); return id; } // insert_segment(l, r, f), in O(log^2 X) void insert_segment(unique_ptr &p, X left, X right, X l, X r, const IndexedFunc &f) { if (r <= left || right <= l) return; if (l <= left && right <= r) { insert(p, left, right, f); return; } if (!p) p = make_unique(); X mid = (left + right) / 2; insert_segment(p->left, left, mid, l, r, f); insert_segment(p->right, mid, right, l, r, f); } int insert_segment(X l, X r, Func f) { assert(XL <= l && l <= r && r <= XR); int id = next_id++; if (l != r) insert_segment(root, XL, XR, l, r, IndexedFunc{std::move(f), id}); return id; } // query(x), in O(log X) pair query(X x) const { assert(XL <= x && x < XR); pair res{INF, -1}; const Node *p = root.get(); X left = XL, right = XR; while (p) { if (p->func) { auto cur = make_pair(p->func->eval(x), p->func->id); if (res.second == -1 || cur < res) res = cur; } if (right - left == 1) break; X mid = (left + right) / 2; if (x < mid) { p = p->left.get(); right = mid; } else { p = p->right.get(); left = mid; } } return res; } }; #line 7 "/Users/otsuki/Documents/GitHub/algorithm/Optimization/monge_shortest_path_by_cht.hpp" // find shortest path on DAG with Pairwise Single Crossing cost, by Li Chao Tree, O(N log N) // vertex: 0, 1, 2, ..., N // A(j, i) = dp[i] + f(i, j) must be Pairwise Single Crossing (must be in all 0 <= i, j <= N, including i >= j) // Attention: Even if f is Pairwise Single Crossing, A is not necessarily Pairwise Single Crossing // edges: (i, j), j >= start[i] (i + 1 <= start[i] <= N + 1), start is non-decreasing template vector> MongeShortestPathByCHT(int N, const FUNC &f, const vector &start) { assert((int)start.size() == N); for (int i = 0; i < N; i++) { assert(i + 1 <= start[i] && start[i] <= N + 1); if (i > 0) assert(start[i - 1] <= start[i]); } VAL INF = numeric_limits::max() / 2; vector> dp(N + 1, make_pair(numeric_limits::max() / 2, -1)); auto make_func = [&](int i) { return [&, i](long long j) -> VAL { return dp[i].first + f(i, j); }; }; DynamicGeneralLiChaoTree cht(1, N + 1, INF); dp[0].first = 0; int i = 0; for (int j = 1; j <= N; j++) { while (i < N && j >= start[i]) { cht.insert(make_func(i)); i++; } dp[j] = cht.query(j); }; return dp; } template vector> MongeShortestPathByCHT(int N, const FUNC &f) { vector start(N); for (int i = 0; i < N; i++) start[i] = i + 1; return MongeShortestPathByCHT(N, f, start); } #line 7 "test.cpp" // yukicoder No.705 ゴミ拾い Hard /* A, X, Y: N 個 これらを区間に分割していく  dp[j] = min_{0 ≦ i < j}(dp[i] + |A[j-1] - X[i]|^3 + |-Y[i]|^3) i -> j のコスト:|A[j-1] - X[i]|^3 + |-Y[i]|^3 ...... 差の凸関数 (Monge) + 縞々 (Monge) -> Monge スタート: 0, ゴール: N */ int main() { int N; cin >> N; vector A(N), X(N), Y(N); for (int i = 0; i < N; i++) cin >> A[i]; for (int i = 0; i < N; i++) cin >> X[i]; for (int i = 0; i < N; i++) cin >> Y[i]; auto func = [&](int i, int j) -> long long { long long dx = abs(A[j-1] - X[i]), dy = abs(Y[i]); return dx * dx * dx + dy * dy * dy; }; auto res = MongeShortestPathByCHT(N, func); cout << res[N].first << endl; }