제출 #1362493

#제출 시각아이디문제언어결과실행 시간메모리
1362493WH8Long Distance Coach (JOI17_coach)C++17
100 / 100
82 ms16048 KiB
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#define ordered_set tree<int, null_type,less<int>, rb_tree_tag,tree_order_statistics_node_update>
using namespace std;
using namespace __gnu_pbds;

#define int long long
#define f first
#define s second
#define pll pair<long long, long long>
#define pb push_back
#define all(x) x.begin(), x.end()

const long long INF = (long long)4e18;

/*
    This is the LineContainer from OI notes, max-query version.
    To query minimum of y = mx + c:
        insert (-m, -c)
        answer = -query(x)
*/
struct Line {
    mutable long long k, m, p; // y = kx + m, p = last x where this line is best
    bool operator<(const Line& o) const {
        return k < o.k;
    }
    bool operator<(long long x) const {
        return p < x;
    }
};

struct LineContainer : multiset<Line, less<>> {
    static const long long inf = LLONG_MAX;

    long long div_floor(long long a, long long b) {
        // floored division
        long long q = a / b;
        long long r = a % b;
        if ((a ^ b) < 0 && r) q--;
        return q;
    }

    bool isect(iterator x, iterator y) {
        if (y == end()) {
            x->p = inf;
            return false;
        }
        if (x->k == y->k) {
            x->p = (x->m > y->m ? inf : -inf);
        } else {
            x->p = div_floor(y->m - x->m, x->k - y->k);
        }
        return x->p >= y->p;
    }

    void add_line(long long k, long long m) {
        auto z = insert({k, m, 0});
        auto y = z++;
        auto x = y;

        while (isect(y, z)) z = erase(z);

        if (x != begin() && isect(--x, y)) {
            isect(x, y = erase(y));
        }

        while ((y = x) != begin() && (--x)->p >= y->p) {
            isect(x, erase(y));
        }
    }

    long long query_max(long long x) {
        assert(!empty());
        auto l = *lower_bound(x);
        return l.k * x + l.m;
    }

    void add_min_line(long long k, long long m) {
        add_line(-k, -m);
    }

    long long query_min(long long x) {
        return -query_max(x);
    }
};

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int X, N, M, W, T;
    cin >> X >> N >> M >> W >> T;

    vector<pll> events; // {residue, quotient}

    for (int i = 0; i < N; i++) {
        int S;
        cin >> S;
        events.pb({S % T, S / T});
    }

    // Treat destination X as a "final refill event":
    // passengers may fail before arrival, but driver must survive.
    events.pb({X % T, X / T});

    vector<pll> p(M + 1); // {D, C}, 1-indexed after sorting
    for (int i = 1; i <= M; i++) {
        cin >> p[i].f >> p[i].s;
    }

    sort(p.begin() + 1, p.end());

    vector<int> prefC(M + 1, 0);
    for (int i = 1; i <= M; i++) {
        prefC[i] = prefC[i - 1] + p[i].s;
    }

    // We process passengers from right to left by D.
    // So process refill/end events from large residue to small residue.
    sort(events.begin(), events.end(), greater<pll>());

    vector<int> dp(M + 2, INF);
    dp[M + 1] = 0;

    LineContainer cht;
    int ptr = 0;

    for (int i = M; i >= 1; i--) {
        int D = p[i].f;

        /*
            Insert every refill/end event with residue > D.

            Suppose this event is S = qT + r, and currently it lies between
            passenger i and i+1 in sorted D-order.

            Let b = i. If later we choose passengers a..b to leave before this event:

                cost = dp[b+1]
                     + (prefC[b] - prefC[a-1])
                     + (b-a+1) * q * W

            Rearrange by a:

                cost = -prefC[a-1]
                     + (-qW) * a
                     + (dp[b+1] + prefC[b] + qW*(b+1))

            So this event contributes one line:
                y = (-qW)x + (dp[b+1] + prefC[b] + qW*(b+1))
        */
        while (ptr < (int)events.size() && events[ptr].f > D) {
            int q = events[ptr].s;
            int b = i;

            int slope = -q * W;
            int intercept = dp[b + 1] + prefC[b] + q * W * (b + 1);

            cht.add_min_line(slope, intercept);
            ptr++;
        }

        // Option 1: passenger i reaches destination.
        int full_drinks = X / T + (X % T > D);
        dp[i] = dp[i + 1] + full_drinks * W;

        // Option 2: passenger i is the start of a block that leaves before some event.
        if (!cht.empty()) {
            dp[i] = min(dp[i], cht.query_min(i) - prefC[i - 1]);
        }
    }

    // Driver drinks at times 0, T, 2T, ..., floor(X/T)T.
    int driver_cost = (X / T + 1) * W;

    cout << dp[1] + driver_cost << '\n';
}
#결과 실행 시간메모리채점기 출력
결과를 불러오는 중입니다…
#결과 실행 시간메모리채점기 출력
결과를 불러오는 중입니다…
#결과 실행 시간메모리채점기 출력
결과를 불러오는 중입니다…
#결과 실행 시간메모리채점기 출력
결과를 불러오는 중입니다…