Submission #1259343

#TimeUsernameProblemLanguageResultExecution timeMemory
1259343ducthanh2306Festival (IOI25_festival)C++20
22 / 100
1127 ms1762104 KiB
#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <optional>

// Use a 128-bit integer type for token calculations to prevent overflow.
using int128 = unsigned __int128;

// Coupon struct holding its original index and properties.
struct Coupon {
    int id;
    long long p;
    int t;
};

// Global sorting function for all coupons.
bool compareCoupons(const Coupon& a, const Coupon& b) {
    bool a_is_mult = a.t > 1;
    bool b_is_mult = b.t > 1;
    if (a_is_mult && !b_is_mult) return true;  // Multipliers first
    if (!a_is_mult && b_is_mult) return false;

    if (a_is_mult) { // Both are multipliers
        return static_cast<int128>(a.p) * a.t * (b.t - 1) < static_cast<int128>(b.p) * b.t * (a.t - 1);
    } else { // Both are spenders
        return a.p < b.p;
    }
}

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

    std::vector<Coupon> sorted_coupons;
    for (int i = 0; i < N; ++i) {
        sorted_coupons.push_back({i, (long long)P[i], T[i]});
    }

    std::sort(sorted_coupons.begin(), sorted_coupons.end(), compareCoupons);

    // dp[k] = max tokens after buying k coupons. Using a 1D DP array optimized in place.
    std::vector<std::optional<int128>> dp(N + 1);
    // parent[k] stores the coupon `id` and the previous `k` to reconstruct the path.
    std::vector<std::pair<int, int>> parent(N + 1, {-1, -1});

    dp[0] = A;

    for (const auto& coupon : sorted_coupons) {
        for (int k = N - 1; k >= 0; --k) {
            if (!dp[k].has_value()) continue;

            int128 current_tokens = dp[k].value();
            if (current_tokens >= coupon.p) {
                int128 new_tokens = (current_tokens - coupon.p) * coupon.t;
                if (!dp[k + 1].has_value() || new_tokens > dp[k + 1].value()) {
                    dp[k + 1] = new_tokens;
                    // To reconstruct, we need to know we came from state k
                    // and used this coupon. This is tricky with 1D DP.
                    // A full 2D parent table is better for clarity.
                }
            }
        }
    }
    
    // The 1D DP above finds the max count, but path reconstruction is hard.
    // The 2D version below is clearer and allows reconstruction. It will be too slow for large N.

    std::vector<std::vector<std::optional<int128>>> dp2D(N + 1, std::vector<std::optional<int128>>(N + 1));
    std::vector<std::vector<int>> path_choice(N + 1, std::vector<int>(N + 1, 0)); // 0 = skip, 1 = take
    
    dp2D[0][0] = A;

    for (int i = 1; i <= N; ++i) {
        const auto& coupon = sorted_coupons[i - 1];
        for (int k = 0; k <= i; ++k) {
            // Option 1: Don't take the current coupon
            dp2D[i][k] = dp2D[i - 1][k];
            path_choice[i][k] = 0;

            // Option 2: Take the current coupon
            if (k > 0) {
                if (dp2D[i - 1][k - 1].has_value() && dp2D[i - 1][k - 1].value() >= coupon.p) {
                    int128 new_tokens = (dp2D[i - 1][k - 1].value() - coupon.p) * coupon.t;
                    if (!dp2D[i][k].has_value() || new_tokens > dp2D[i][k].value()) {
                        dp2D[i][k] = new_tokens;
                        path_choice[i][k] = 1;
                    }
                }
            }
        }
    }

    // Find the maximum number of coupons bought
    int max_k = 0;
    for (int k = N; k >= 0; --k) {
        if (dp2D[N][k].has_value()) {
            max_k = k;
            break;
        }
    }

    // Reconstruct the path by backtracking
    std::vector<int> result;
    int current_k = max_k;
    for (int i = N; i > 0 && current_k > 0; --i) {
        if (path_choice[i][current_k] == 1) {
            result.push_back(sorted_coupons[i - 1].id);
            current_k--;
        }
    }

    // The reconstructed path is in reverse order of processing.
    // The problem requires chronological order of purchase.
    // Since our processing order *is* the optimal purchase order, we just need to reverse the result.
    std::reverse(result.begin(), result.end());

    return result;
}
#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...