From 785b8595694a53a649b4207904e4d28de6e2c152 Mon Sep 17 00:00:00 2001 From: KowerKoint Date: Wed, 17 Apr 2024 18:18:20 +0900 Subject: [PATCH] add monotone_minima, online_offline_dp --- cpp/convex.hpp | 76 ++++++++++++++++++++++++ test/atcoder-abc177-f.1.test.cpp | 33 ---------- test/atcoder-colopl2018_final-c.test.cpp | 16 +++++ test/yukicoder-705.test.cpp | 18 ++++++ 4 files changed, 110 insertions(+), 33 deletions(-) create mode 100644 cpp/convex.hpp delete mode 100644 test/atcoder-abc177-f.1.test.cpp create mode 100644 test/atcoder-colopl2018_final-c.test.cpp create mode 100644 test/yukicoder-705.test.cpp diff --git a/cpp/convex.hpp b/cpp/convex.hpp new file mode 100644 index 0000000..2329774 --- /dev/null +++ b/cpp/convex.hpp @@ -0,0 +1,76 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +/** + * @brief monotoneな行列において、各行の最小値を取る最小列番号を$O((H+W)\log H)$時間で得る + * @param h 行数(行番号は[0..h-1]) + * @param w 列数(列番号は[0..w-1]) + * @param f 行列の要素を与える関数(行番号と列番号を引数にとって値を返す) + * @param comp 要素の比較関数(最小値を知りたい場合はデフォルトのstd::less) + * @return std::vector 各行の最小値列番号 + */ +template >> +std::vector monotone_minima(int h, int w, const F& f, const Comp& comp = Comp()) { + using T = std::invoke_result_t; + assert(h >= 0); + assert(w >= 0); + std::vector res(h); + std::stack> stk; // {i, j, l, r} : [i..j]行目の答えを求める。結果は[l..r]の範囲に収まることが保証されている。 + stk.emplace(0, h-1, 0, w-1); + while(!stk.empty()) { + auto [i, j, l, r] = stk.top(); stk.pop(); + int m = (i+j) / 2; + T min_value = f(m, l); + int min_idx = l; + for(int k = l+1; k <= r; k++) { + T value = f(m, k); + if(comp(value, min_value)) { + min_value = value; + min_idx = k; + } + } + res[m] = min_idx; + if(i <= m-1) stk.emplace(i, m-1, l, min_idx); + if(m+1 <= j) stk.emplace(m+1, j, min_idx, r); + } + return res; +} + +/** + * @brief オンライン・オフライン変換による、DAGで辺のコストがmonotoneな場合の最短経路問題 + * @param n ノード数-1 (ノード番号は[0..n]) + * @param f 辺のコスト(i>> +std::vector> online_offline_dp(int n, const F& f, const Comp& comp = Comp{}) { + using T = std::invoke_result_t; + std::vector> dp(n+1, std::nullopt); + dp[0] = 0; + // dp[l..r)の要素を、dp[l..r)からの遷移のみの範囲で求める + auto solve_subproblem = [&](auto self, int l, int r) -> void { + int mid = (l+r) / 2; + if(mid-l >= 2) self(self, l, mid); + auto submat = [&](int i, int j) { + return dp[l+j].value() + f(l+j, mid+i); + }; + std::vector min_idx = monotone_minima(r-mid, mid-l, submat, comp); + for(int i = 0; i < r-mid; i++) { + T val = submat(i, min_idx[i]); + if(!dp[mid+i] || comp(val, dp[mid+i].value())) { + dp[mid+i] = val; + } + } + if(r-mid >= 2) self(self, mid, r); + }; + solve_subproblem(solve_subproblem, 0, n+1); + std::vector res(n+1); + for(int i = 0; i <= n; i++) res[i] = dp[i].value(); + return res; +} diff --git a/test/atcoder-abc177-f.1.test.cpp b/test/atcoder-abc177-f.1.test.cpp deleted file mode 100644 index c209f3a..0000000 --- a/test/atcoder-abc177-f.1.test.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#define PROBLEM "https://atcoder.jp/contests/abc177/tasks/abc177_f" - -#include - -#include "../cpp/lazy-segtree.hpp" - -int main(void) { - int h, w; std::cin >> h >> w; - struct Mapping { - int operator() (int f, int x, int l, int r) { - int id = lazy_segtree::MaxLimit{}(); - return f == id ? x : f + l; - } - }; - struct Composition { - int operator() (int f, int g) { - int id = lazy_segtree::MaxLimit{}(); - return f == id ? g : f; - } - }; - StaticLazySegTree, lazy_segtree::MaxLimit, int, Mapping, Composition, lazy_segtree::MaxLimit> seg(std::vector(w, 0)); - for(int i = 0; i < h; i++) { - int a, b; std::cin >> a >> b; a--; - if(a == 0) { - // unreachable to [a,b) - seg.apply(a, b, 1000000000); - } else { - seg.apply(a, b, seg[a-1] + 1 - a); - } - int ans = seg.all_prod(); - std::cout << (ans >= 1000000000 ? -1 : ans+i+1) << '\n'; - } -} \ No newline at end of file diff --git a/test/atcoder-colopl2018_final-c.test.cpp b/test/atcoder-colopl2018_final-c.test.cpp new file mode 100644 index 0000000..7cce802 --- /dev/null +++ b/test/atcoder-colopl2018_final-c.test.cpp @@ -0,0 +1,16 @@ +#include "../cpp/convex.hpp" +#include + +int main() { + using ll = long long; + int n; std::cin >> n; + std::vector a(n); + for(int i = 0; i < n; i++) std::cin >> a[i]; + auto f = [&](int i, int j) { + return a[j] + (ll)(j-i)*(j-i); + }; + std::vector minj = monotone_minima(n, n, f); + for(int i = 0; i < n; i++) { + std::cout << f(i, minj[i]) << std::endl; + } +} diff --git a/test/yukicoder-705.test.cpp b/test/yukicoder-705.test.cpp new file mode 100644 index 0000000..6ccacdb --- /dev/null +++ b/test/yukicoder-705.test.cpp @@ -0,0 +1,18 @@ +#define PROBLEM "https://yukicoder.me/problems/no/705" +#include "../cpp/convex.hpp" +#include + +int main() { + using ll = long long; + int n; std::cin >> n; + std::vector a(n), x(n), y(n); + for(int i = 0; i < n; i++) std::cin >> a[i]; + for(int i = 0; i < n; i++) std::cin >> x[i]; + for(int i = 0; i < n; i++) std::cin >> y[i]; + auto f = [&](int i, int j) { + ll dx = std::abs(x[i] - a[j-1]); + ll dy = std::abs(y[i]); + return dx*dx*dx + dy*dy*dy; + }; + std::cout << online_offline_dp(n, f)[n] << std::endl; +}