#line 1 "test.cpp" #define PROBLEM "https://yukicoder.me/problems/no/952" #include using namespace std; #line 4 "https://drken1215.github.io/algorithm/Optimization/monge_enum_d_shortest_paths.hpp.html" using namespace std; #line 4 "https://drken1215.github.io/algorithm/Optimization/monotone_minima.hpp.html" using namespace std; // find min_j f(i, j) for all i, by Monotone Minima, O(H + W log H) // f(i, j) must be monotone (argmin is not decreasing) // slide access OK template void MonotoneMinimaRec (int HL, int HR, int WL, int WR, const FUNC &f, vector> &res) { if (HR - HL <= 0) return; int HM = (HL + HR) / 2; res[HM].second = WL; for (int i = WL; i < WR; i++) { VAL val = f(HM, i); if (res[HM].first > val) res[HM] = make_pair(val, i); } MonotoneMinimaRec(HL, HM, WL, res[HM].second + 1, f, res); MonotoneMinimaRec(HM + 1, HR, res[HM].second, WR, f, res); } template vector> MonotoneMinima(int H, int W, const FUNC &f) { vector> res(H, make_pair(numeric_limits::max() / 2, -1)); MonotoneMinimaRec(0, H, 0, W, f, res); return res; } #line 4 "https://drken1215.github.io/algorithm/Optimization/smawk.hpp.html" using namespace std; // find min_j f(i, j) for all i, by SMAWK, O(H + W) // f(i, j) must be totally monotone // slide access NG template void SMAWKRec (const vector &X, const vector &Y, const FUNC &f, vector> &res) { if (X.empty()) return; // Reduce Step vector X2, Y2; for (auto y : Y) { while (!Y2.empty()) { int py = Y2.back(), x = X[(int)Y2.size() - 1]; if (f(x, y) >= f(x, py)) break; Y2.pop_back(); } if (Y2.size() < X.size()) Y2.emplace_back(y); } // Recurse Step for (int i = 1; i < (int)X.size(); i += 2) X2.emplace_back(X[i]); SMAWKRec(X2, Y2, f, res); // Interpolate Step int p = 0; for (int i = 0; i < (int)X.size(); i += 2) { int lim = (i + 1 < (int)X.size() ? res[X[i + 1]].second : Y.back()), best = Y[p]; while (Y[p] < lim) { p++; if (f(X[i], Y[p]) < f(X[i], best)) best = Y[p]; } res[X[i]] = {f(X[i], best), best}; } } template vector> SMAWK(int H, int W, const FUNC &f) { if (H == 0) return {}; assert(W > 0); vector> res(H, make_pair(numeric_limits::max() / 2, -1)); vector X(H), Y(W); for (int i = 0; i < H; i++) X[i] = i; for (int j = 0; j < W; j++) Y[j] = j; SMAWKRec(X, Y, f, res); return res; } #line 8 "https://drken1215.github.io/algorithm/Optimization/monge_enum_d_shortest_paths.hpp.html" // find the d-edges shortest path for d = 1, 2, ..., D, in O(ND) // vertex: 0, 1, 2, ..., N // f(i, j) must be Monge (Then, A(j, i) = dp[i] + f(i, j) is also Monge) // you can choose SMAWK (random access) or Monotone Minima (slide access) template struct MongeShortestPathWithDEdges { VAL INF = numeric_limits::max() / 2; // solver (random access ver) template vector>> solve(int N, const FUNC &f, int D, const string &solver = "smawk") { assert(D <= N); vector>> res(N + 1, vector>(D + 1, make_pair(INF, -1))); vector dp(N + 1, INF); res[0][0].first = 0, dp[0] = 0; auto f2 = [&](int i, int j) -> VAL { return (j < i ? dp[j] + f(j, i) : INF); }; for (int d = 1; d <= D; d++) { vector> tmp; if (solver == "smawk") tmp = SMAWK(N + 1, N + 1, f2); else if (solver == "monotone") tmp = MonotoneMinima(N + 1, N + 1, f2); else { cout << "no solver" << endl; assert(true); } for (int i = d; i <= N; i++) { dp[i] = tmp[i].first; res[i][d] = tmp[i]; } } return res; } // solver (slide access ver) template vector>> solve (int N, const ADD &add, const DEL &del, const GETCOST &get, int D, const string &solver = "monotone") { assert(D <= N); vector>> res(N + 1, vector>(D + 1, make_pair(INF, -1))); vector dp(N + 1, INF); res[0][0].first = 0, dp[0] = 0; int left = 0, right = 0; auto f = [&](int i, int j) -> long long { while (left < i) del(left++); while (i < left) add(--left); while (right < j) add(right++); while (j < right) del(--right); return get(); }; auto f2 = [&](int i, int j) -> VAL { return (j < i ? dp[j] + f(j, i) : INF); }; for (int d = 1; d <= D; d++) { vector> tmp; if (solver == "monotone") tmp = MonotoneMinima(N + 1, N + 1, f2); else { cout << "no solver" << endl; assert(true); } for (int i = d; i <= N; i++) { dp[i] = tmp[i].first; res[i][d] = tmp[i]; } } return res; } }; #line 7 "test.cpp" // yukicoder No.952 危険な火薬庫 /* 末尾にドアを追加する。 ノード 0, 1, ..., N, N+1 f(i, j) = (S[j-1] - S[i])^2 選ぶドアが k 個 → 区間の個数は、N+1-k */ int main() { int N; cin >> N; vector A(N + 1, 0), S(N + 2, 0); for (int i = 0; i < N; i++) cin >> A[i], S[i + 1] = S[i] + A[i]; auto f = [&](int i, int j) -> long long { return (S[j - 1] - S[i]) * (S[j - 1] - S[i]); }; MongeShortestPathWithDEdges msp; auto res = msp.solve(N + 1, f, N + 1); for (int k = 1; k <= N; k++) cout << res[N + 1][N + 1 - k].first << endl; }