#include "souvenirs.h"
#include <vector>
#include <algorithm>
#include <numeric>
#include <cassert>
using namespace std;
// transaction(M) ↦ { list of bought types in increasing order, remainder }
// 1) Learn P[1..N-1] by binary search with offset-sums.
//    We maintain prefix-sum S = sum_{j< i} P[j].  To test P[i], we
//    hand M = S + mid.  Because mid < P[i-1], the seller will first
//    remove exactly P[1],…,P[i-1], leaving mid in the pile, so the
//    only possible purchase is type i.  Checking its presence tells us
//    whether mid >= P[i].
// 2) Build a list of required items: i copies of price P[i] (for i=1..N-1).
//    Do a first-fit‐decreasing pack into bins of capacity P0-1, disallowing
//    two of the same type in one bin.  Each bin becomes one transaction
//    (hand exactly the sum of its weights) and buys exactly those types.
void buy_souvenirs(int N, long long P0) {
    vector<long long> P(N);
    P[0] = P0;
    // --- 1) Learn all P[i] ---
    long long prefixSum = 0;
    for (int i = 1; i < N; i++) {
        long long lo = 1, hi = P[i-1] - 1;
        // binary search P[i] in [1 .. P[i-1]-1]
        while (lo < hi) {
            long long mid = (lo + hi) >> 1;
            auto res = transaction(prefixSum + mid);
            const auto &bought = res.first;
            // did we buy type i?
            bool got = binary_search(bought.begin(), bought.end(), i);
            if (got) {
                // mid >= P[i]
                hi = mid;
            } else {
                lo = mid + 1;
            }
        }
        P[i] = lo;
        prefixSum += P[i];
    }
    // --- 2) Pack i copies of price P[i] into bins ---
    struct Bin {
        long long rem;
        vector<bool> used;  // used[type] = true once that type is in this bin
        Bin(long long cap, int N): rem(cap), used(N,false) {}
    };
    vector<Bin> bins;
    // Build a list of (type,weight) entries
    vector<pair<int,long long>> items;
    for (int i = 1; i < N; i++) {
        for (int cnt = 0; cnt < i; cnt++) {
            items.emplace_back(i, P[i]);
        }
    }
    // Sort by descending weight so FFD works best
    sort(items.begin(), items.end(),
         [&](auto &a, auto &b){ return a.second > b.second; });
    // First-fit: for each item, try each bin, else open new
    for (auto &it : items) {
        int t = it.first;
        long long w = it.second;
        bool placed = false;
        for (auto &B : bins) {
            if (!B.used[t] && B.rem >= w) {
                B.used[t] = true;
                B.rem -= w;
                placed = true;
                break;
            }
        }
        if (!placed) {
            bins.emplace_back(P0-1, N);
            bins.back().used[t] = true;
            bins.back().rem -= w;
        }
    }
    // 3) Finally, for each bin, hand exactly (P0-1 - rem) coins,
    //    which is the sum of its selected weights.
    for (auto &B : bins) {
        long long M = (P0 - 1) - B.rem;
        auto res = transaction(M);
        // we ignore the result vector here; it must match exactly our
        // chosen set for this bin by construction.
    }
}
| # | Verdict  | Execution time | Memory | Grader output | 
|---|
| Fetching results... | 
| # | Verdict  | Execution time | Memory | Grader output | 
|---|
| Fetching results... | 
| # | Verdict  | Execution time | Memory | Grader output | 
|---|
| Fetching results... | 
| # | Verdict  | Execution time | Memory | Grader output | 
|---|
| Fetching results... | 
| # | Verdict  | Execution time | Memory | Grader output | 
|---|
| Fetching results... | 
| # | Verdict  | Execution time | Memory | Grader output | 
|---|
| Fetching results... | 
| # | Verdict  | Execution time | Memory | Grader output | 
|---|
| Fetching results... |