結果

問題 No.703 ゴミ拾い Easy
ユーザー parenthesesparentheses
提出日時 2021-10-13 20:13:20
言語 C++17
(gcc 12.3.0 + boost 1.83.0)
結果
AC  
実行時間 112 ms / 1,500 ms
コード長 4,363 bytes
コンパイル時間 4,118 ms
コンパイル使用メモリ 211,688 KB
実行使用メモリ 37,328 KB
最終ジャッジ日時 2023-10-17 19:10:28
合計ジャッジ時間 11,803 ms
ジャッジサーバーID
(参考情報)
judge11 / judge13
このコードへのチャレンジ
(要ログイン)

テストケース

テストケース表示
入力 結果 実行時間
実行使用メモリ
testcase_00 AC 2 ms
4,348 KB
testcase_01 AC 1 ms
4,348 KB
testcase_02 AC 2 ms
4,348 KB
testcase_03 AC 2 ms
4,348 KB
testcase_04 AC 2 ms
4,348 KB
testcase_05 AC 2 ms
4,348 KB
testcase_06 AC 2 ms
4,348 KB
testcase_07 AC 2 ms
4,348 KB
testcase_08 AC 2 ms
4,348 KB
testcase_09 AC 2 ms
4,348 KB
testcase_10 AC 2 ms
4,348 KB
testcase_11 AC 2 ms
4,348 KB
testcase_12 AC 2 ms
4,348 KB
testcase_13 AC 2 ms
4,348 KB
testcase_14 AC 2 ms
4,348 KB
testcase_15 AC 2 ms
4,348 KB
testcase_16 AC 2 ms
4,348 KB
testcase_17 AC 3 ms
4,348 KB
testcase_18 AC 2 ms
4,348 KB
testcase_19 AC 2 ms
4,348 KB
testcase_20 AC 2 ms
4,348 KB
testcase_21 AC 2 ms
4,348 KB
testcase_22 AC 2 ms
4,348 KB
testcase_23 AC 2 ms
4,348 KB
testcase_24 AC 112 ms
37,328 KB
testcase_25 AC 111 ms
37,328 KB
testcase_26 AC 111 ms
37,328 KB
testcase_27 AC 111 ms
37,328 KB
testcase_28 AC 110 ms
37,328 KB
testcase_29 AC 110 ms
37,328 KB
testcase_30 AC 110 ms
37,328 KB
testcase_31 AC 112 ms
37,328 KB
testcase_32 AC 111 ms
37,328 KB
testcase_33 AC 111 ms
37,328 KB
testcase_34 AC 97 ms
37,328 KB
testcase_35 AC 97 ms
37,328 KB
testcase_36 AC 97 ms
37,328 KB
testcase_37 AC 98 ms
37,328 KB
testcase_38 AC 97 ms
37,328 KB
testcase_39 AC 97 ms
37,328 KB
testcase_40 AC 98 ms
37,328 KB
testcase_41 AC 97 ms
37,328 KB
testcase_42 AC 98 ms
37,328 KB
testcase_43 AC 97 ms
37,328 KB
testcase_44 AC 2 ms
4,348 KB
testcase_45 AC 2 ms
4,348 KB
testcase_46 AC 109 ms
37,328 KB
testcase_47 AC 108 ms
37,328 KB
testcase_48 AC 2 ms
4,348 KB
testcase_49 AC 3 ms
4,348 KB
権限があれば一括ダウンロードができます

ソースコード

diff #

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
// --------------------------------------------------------
template<class T> bool chmin(T& a, const T b) { if (b < a) { a = b; return 1; } return 0; }
#define FOR(i,l,r) for (ll i = (l); i < (r); ++i)
#define REP(i,n) FOR(i,0,n)
using VB = vector<bool>;
using VLL = vector<ll>;
// --------------------------------------------------------


// References:
//   <https://smijake3.hatenablog.com/entry/2018/06/16/144548>
//   <https://tjkendev.github.io/procon-library/cpp/convex_hull_trick/li_chao_tree.html>
//   <https://cp-algorithms.com/geometry/convex_hull_trick.html>

/**
 * @brief Convex Hull Trick (Li-Chao Segment Tree)
 * 
 */
struct LiChaoSegtree {
    static constexpr ll INF1 = 1e9;   // 葉ノード以外のダミー座標
    static constexpr ll INF2 = 1e18;  // 最小値クエリの初期値
    int N;          // 座標の数
    vector<ll> xs, p, q;  // 座標・傾き・接線
    vector<bool> used;    // ノードが一度も使用されていなければ false

    LiChaoSegtree(int n, const vector<ll>& ps) {
        N = 1;
        while (N < n) N <<= 1;

        xs.resize(2*N);
        p.resize(2*N);
        q.resize(2*N);
        used.resize(2*N);

        for (int i = 0; i < n; i++) xs[i] = ps[i];
        for (int i = n; i < 2*N; i++) xs[i] = INF1;
        for (int i = 0; i < 2*N; i++) used[i] = false;
    }

    // 区間 [l,r) に対する直線 (a,b) の追加処理
    void _add_line(ll a, ll b, int k, int l, int r) {
        while (l < r) {
            if(not used[k]) {
                used[k] = true; p[k] = a; q[k] = b;
                return;
            }

            int m = (l + r) / 2;
            ll lx = xs[l], mx = xs[m], rx = xs[r-1];
            ll pk = p[k], qk = q[k];
            bool left  = (a*lx + b < pk*lx + qk);
            bool mid   = (a*mx + b < pk*mx + qk);
            bool right = (a*rx + b < pk*rx + qk);

            if (left && right) {  // 直線 (a,b) が全勝
                p[k] = a;
                q[k] = b;
                return;
            } else if (not left && not right) {  // 直線 (p,q) が全勝
                return;
            } else if (mid) {  // swap することで探索区間を片側だけに減らすテク
                swap(p[k], a);
                swap(q[k], b);
            } else if (left != mid) {  // [l,m) で直線 (a,b) が勝つ部分あり
                k = 2*k + 1; r = m;
            } else {  // [m,r) で直線 (a,b) が勝つ部分あり
                k = 2*k + 2; l = m;
            }
        }
    }

    // 直線 (a,b) の追加
    void add_line(ll a, ll b) { _add_line(a, b, 0, 0, N); }

    // 区間 [x_l, x_r) に対する線分 (a,b) の追加
    void add_segment_line(ll a, ll b, int l, int r) {
        int L = l + N, R = r + N;
        int sz = 1;
        while (L < R) {
            if (L & 1) {
                _add_line(a, b, L-1, l, l+sz);
                L++; l += sz;
            }
            if (R & 1) {
                R--; r -= sz;
                _add_line(a, b, R-1, r, r+sz);
            }
            L >>= 1; R >>= 1;
            sz <<= 1;
        }
    }

    // i 番目の座標に対する最小値を返す
    ll query(int i) {
        ll x = xs[i];
        int k = i + (N - 1);
        ll res = (used[k] ? p[k]*x + q[k] : INF2);
        while (k > 0) {
            k = (k - 1) / 2;
            if (used[k]) chmin(res, p[k]*x + q[k]);
        }
        return res;
    }
};


int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout << fixed << setprecision(15);

    ll N; cin >> N;
    VLL a(N); REP(i,N) cin >> a[i];
    VLL x(N); REP(i,N) cin >> x[i];
    VLL y(N); REP(i,N) cin >> y[i];

    LiChaoSegtree cht(N, a);

    // dp[i] :=  左から i 番目までを考えた時の最小コスト
    // ----- 遷移式 --> Convex Hull Trick -----
    // dp[0] = 0
    // dp[i] = min_{0<=j<i} { dp[j] + (x[j] - a[i-1])^2 + y[j]^2 }
    //       = min_{0<=j<i} { -2*x[j]*a[i-1] + (dp[j] + x[j]^2 + y[j]^2) } + a[i-1]^2
    //       (1<=i<=N)
    VLL dp(N+1); dp[0] = 0;
    REP(i,N) {
        cht.add_line(-2*x[i], dp[i] + x[i]*x[i] + y[i]*y[i]);
        dp[i+1] = cht.query(i) + a[i]*a[i];
    }
    ll ans = dp[N];
    cout << ans << endl;

    return 0;
}
// Verify: https://yukicoder.me/problems/no/703
0