Submission #1342116

#TimeUsernameProblemLanguageResultExecution timeMemory
1342116mehrzadFestival (IOI25_festival)C++17
100 / 100
350 ms153980 KiB
#include <bits/stdc++.h>
using namespace std;

struct Coupon {
    int idx;               // original index
    long long price;       // P[i]
    int type;              // T[i] (1..4)
};

struct State {
    unsigned long long token; // tokens after this step
    int cnt;                  // number of multi‑type coupons taken so far
    int prev;                 // index of previous state (from previous step)
    bool taken;               // was this coupon taken at its step?
    int step;                 // step number (0..M)
};

struct Candidate {
    unsigned long long token;
    int cnt;
    int prev;
    bool taken;
    int step;
};

std::vector<int> max_coupons(int A, std::vector<int> P,
                             std::vector<int> T) {
    const int N = (int)P.size();

    // split coupons into multi (type>1) and type1 (type==1)
    vector<Coupon> multi, type1;
    for (int i = 0; i < N; ++i) {
        Coupon c{ i, P[i], T[i] };
        if (T[i] == 1) type1.push_back(c);
        else multi.push_back(c);
    }

    // sort multi coupons by increasing w = (price * type) / (type-1)
    auto cmpMulti = [](const Coupon& a, const Coupon& b) {
        __int128 left  = (__int128)a.price * a.type * (b.type - 1);
        __int128 right = (__int128)b.price * b.type * (a.type - 1);
        if (left != right) return left < right;
        return a.idx < b.idx;
    };
    sort(multi.begin(), multi.end(), cmpMulti);

    // sort type1 coupons by price (cheapest first) for later greedy buying
    auto cmpType1 = [](const Coupon& a, const Coupon& b) {
        if (a.price != b.price) return a.price < b.price;
        return a.idx < b.idx;
    };
    sort(type1.begin(), type1.end(), cmpType1);

    // prefix sums of type1 prices (for fast query how many can be bought)
    int M1 = (int)type1.size();
    vector<unsigned long long> pref(M1 + 1, 0);
    for (int i = 0; i < M1; ++i) pref[i + 1] = pref[i] + (unsigned long long)type1[i].price;

    // ----- DP over multi coupons -----
    const unsigned long long INF = 4'000'000'000'000'000'000ULL; // big enough

    vector<State> states;
    states.reserve(5000);
    states.push_back({ (unsigned long long)A, 0, -1, false, 0 }); // root state

    int M = (int)multi.size();
    vector<vector<int>> dp(M + 1);
    dp[0].push_back(0); // only the root state

    for (int i = 1; i <= M; ++i) {
        const Coupon& c = multi[i - 1];
        vector<Candidate> cand;
        cand.reserve(dp[i - 1].size() * 2);

        for (int sid : dp[i - 1]) {
            const State& st = states[sid];
            // skip this coupon
            cand.push_back({ st.token, st.cnt, sid, false, i });
            // try to take it
            if (st.token >= (unsigned long long)c.price) {
                unsigned long long diff = st.token - (unsigned long long)c.price;
                __int128 prod = (__int128)diff * c.type;
                unsigned long long newTok = (prod > INF ? INF : (unsigned long long)prod);
                cand.push_back({ newTok, st.cnt + 1, sid, true, i });
            }
        }

        // keep only non‑dominated states (Pareto frontier)
        sort(cand.begin(), cand.end(),
             [](const Candidate& a, const Candidate& b) {
                 if (a.token != b.token) return a.token > b.token; // descending token
                 return a.cnt > b.cnt;
             });

        vector<int> newIds;
        newIds.reserve(cand.size());
        int bestCntSoFar = -1;
        for (const auto& cc : cand) {
            if (cc.cnt > bestCntSoFar) {
                State ns;
                ns.token = cc.token;
                ns.cnt   = cc.cnt;
                ns.prev  = cc.prev;
                ns.taken = cc.taken;
                ns.step  = cc.step;
                int nid = (int)states.size();
                states.push_back(ns);
                newIds.push_back(nid);
                bestCntSoFar = cc.cnt;
            }
        }
        dp[i] = std::move(newIds);
    }

    // ----- choose best final state -----
    int bestState = -1;
    int bestTotal = -1;
    for (int sid : dp[M]) {
        const State& st = states[sid];
        int canBuyType1 = (int)(upper_bound(pref.begin(), pref.end(), st.token) - pref.begin() - 1);
        int total = st.cnt + canBuyType1;
        if (total > bestTotal) {
            bestTotal = total;
            bestState = sid;
        }
    }

    // ----- reconstruct answer -----
    vector<int> answer;

    // multi coupons (in the order we processed them)
    int cur = bestState;
    while (states[cur].step > 0) {
        if (states[cur].taken) {
            int stepIdx = states[cur].step - 1; // index in multi vector
            answer.push_back(multi[stepIdx].idx);
        }
        cur = states[cur].prev;
    }
    reverse(answer.begin(), answer.end());

    // type1 coupons: buy cheapest ones possible
    unsigned long long token_end = states[bestState].token;
    int k = (int)(upper_bound(pref.begin(), pref.end(), token_end) - pref.begin() - 1);
    for (int i = 0; i < k; ++i) answer.push_back(type1[i].idx);

    return answer;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...