제출 #1254710

#제출 시각아이디문제언어결과실행 시간메모리
1254710Yelarys선물 (IOI25_souvenirs)C++20
3 / 100
12 ms412 KiB
#include <bits/stdc++.h> using namespace std; // ----- Judge-provided API (do not modify these signatures) ----- pair<vector<int>, long long> transaction(long long M); void buy_souvenirs(int N, long long P0); // ====================================================================== // If you want to test locally with the sample grader format, you can // compile with -DLOCAL and provide a simple simulator below. // Comment this whole LOCAL block out on the real judge. // ====================================================================== #ifdef LOCAL static int gN; static vector<long long> gP; // true prices static long long gP0; pair<vector<int>, long long> transaction(long long M) { // Validate constraints long long P0 = gP[0]; long long Pmin = gP.back(); if (!(P0 > M && M >= Pmin)) { cerr << "Invalid argument in transaction: M=" << M << " P0=" << P0 << " Pmin=" << Pmin << "\n"; exit(0); } long long pile = M; vector<int> L; for (int i = 0; i < gN; ++i) { if (pile >= gP[i]) { if (i == 0) { // should never happen since M < P0 // no-op, but guard } pile -= gP[i]; L.push_back(i); } } // The problem says L is returned in increasing order (we already add in increasing i) return {L, pile}; } // A small local main for testing: reads full P, then runs our solution and prints counts. int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> gN; gP.resize(gN); for (int i = 0; i < gN; ++i) cin >> gP[i]; gP0 = gP[0]; // We'll wrap the counting by intercepting transaction calls: // Track counts per type as we go to print final answer like the sample grader expects. static vector<long long> buyCount; buyCount.assign(gN, 0); // Interpose transaction to count auto orig_transaction = transaction; function<pair<vector<int>, long long>(long long)> hooked = [&](long long M) { auto res = orig_transaction(M); for (int x : res.first) buyCount[x]++; return res; }; // Replace the global function pointer (simulate by capturing lambda in a wrapper) // In this simple LOCAL harness, we just shadow the name via another function. // We'll provide a local alias and call that in buy_souvenirs_local. // To avoid refactoring, define a macro to redirect: #undef transaction #define transaction(M) hooked(M) buy_souvenirs(gN, gP0); for (int i = 0; i < gN; ++i) { if (i) cout << ' '; cout << buyCount[i]; } cout << "\n"; return 0; } #endif // ====================================================================== // Helper: simulate a transaction for a *candidate* price array P with M static pair<vector<int>, long long> simulate_with_P(const vector<long long>& Pcand, long long M) { long long pile = M; vector<int> L; int N = (int)Pcand.size(); for (int i = 0; i < N; ++i) { if (pile >= Pcand[i]) { pile -= Pcand[i]; L.push_back(i); } } return {L, pile}; } void buy_souvenirs(int N, long long P0) { // Target counts: q[i] = i, and q[0] = 0 vector<int> need(N, 0); for (int i = 1; i < N; ++i) need[i] = i; // Current counts (we'll update as we transact) vector<int> got(N, 0); // Build all candidate price sequences consistent with constraints: // P[0] = P0, strictly decreasing, each P[i] in [1..10], distinct. // So pick N-1 distinct numbers from [1..P0-1], sort descending, append P0 at front. vector<vector<long long>> candidates; vector<int> vals; for (int v = 1; v <= (int)P0 - 1; ++v) vals.push_back(v); // choose N-1 of these // we select combinations in descending order later int K = (int)vals.size(); if (N - 1 > K) { // invalid input combo; but assume judge inputs are valid } else { vector<int> mask(K, 0); fill(mask.begin(), mask.begin() + (N - 1), 1); do { vector<int> pick; for (int i = 0; i < K; ++i) if (mask[i]) pick.push_back(vals[i]); sort(pick.begin(), pick.end(), greater<int>()); vector<long long> P; P.reserve(N); P.push_back(P0); for (int x : pick) P.push_back(x); // ensure strictly decreasing bool ok = true; for (int i = 0; i + 1 < N; ++i) if (!(P[i] > P[i + 1])) ok = false; if (ok) candidates.push_back(P); } while (prev_permutation(mask.begin(), mask.end())); } // Known (agreed) prices (excluding P0 which is known) vector<long long> knownP(N, -1); knownP[0] = P0; auto recompute_agreed = [&]() { // For each i, if all candidates have same P[i], set knownP[i] for (int i = 1; i < N; ++i) { long long v = candidates[0][i]; bool same = true; for (size_t c = 1; c < candidates.size(); ++c) { if (candidates[c][i] != v) { same = false; break; } } if (same) knownP[i] = v; } }; recompute_agreed(); auto all_done = [&]() { for (int i = 1; i < N; ++i) if (got[i] < need[i]) return false; return true; }; // Helper: attempt to finish purchasing type i using known price auto finish_type_if_known = [&](int i) { if (i == 0) return; if (knownP[i] != -1) { int rem = need[i] - got[i]; while (rem > 0) { auto [L, R] = transaction(knownP[i]); // buys exactly type i, once (void)R; for (int x : L) got[x]++; rem--; } } }; // While we still have ambiguity or unfinished counts, keep going while (!all_done()) { // Finish any type whose price is known (and still has remaining need) bool progressed = false; for (int i = 1; i < N; ++i) { if (knownP[i] != -1 && got[i] < need[i]) { finish_type_if_known(i); progressed = true; } } if (all_done()) break; // Check if all prices known bool all_prices_known = true; for (int i = 1; i < N; ++i) if (knownP[i] == -1) { all_prices_known = false; break; } if (all_prices_known) { // Just finish any leftovers for (int i = 1; i < N; ++i) finish_type_if_known(i); break; } // Choose a SAFE M (valid for all remaining candidates) that: // (1) doesn't exceed remaining capacities for any candidate, // (2) splits candidates as much as possible. long long lowerSafe = 0; for (auto &Pc : candidates) lowerSafe = max(lowerSafe, Pc.back()); // max Pmin among candidates long long upperSafe = P0 - 1; // Precompute candidate outputs per M and whether they are capacity-safe struct Key { vector<int> L; long long R; bool operator<(Key const& o) const { if (R != o.R) return R < o.R; return L < o.L; } }; long long bestM = -1; int bestDiversity = -1; for (long long M = lowerSafe; M <= upperSafe; ++M) { bool capacity_ok = true; map<Key, int> bucket; for (auto &Pc : candidates) { auto [L, R] = simulate_with_P(Pc, M); // capacity check: this transaction should not cause any got[i] to exceed need[i] bool ok_for_this_candidate = true; // NOTE: Type 0 must never be included since M < P0; simulation respects that. for (int i : L) { if (got[i] + 1 > need[i]) { ok_for_this_candidate = false; break; } } if (!ok_for_this_candidate) { capacity_ok = false; break; } bucket[{L, R}]++; } if (!capacity_ok) continue; int diversity = (int)bucket.size(); if (diversity > bestDiversity) { bestDiversity = diversity; bestM = M; } } if (bestM == -1) { // No safe discriminating M found that respects capacities. // Fall back: try to *force* agreement by finishing any single type whose price // is agreed (if any remaining). If none, pick the smallest i with the smallest // agreed price across candidates (they must agree on some price eventually). // In practice with tiny domains, there will be an agreed price quickly. But just in case: bool did_any = false; recompute_agreed(); for (int i = 1; i < N; ++i) { if (knownP[i] != -1 && got[i] < need[i]) { finish_type_if_known(i); did_any = true; } } if (did_any) continue; // As a last resort: pick an i such that *all candidates* have the same P[i] // (this is what knownP tracks); if none exist (extremely unlikely here), just // pick M = lowerSafe to continue; capacity shouldn’t be violated since we already tried. for (int i = 1; i < N; ++i) { if (knownP[i] != -1 && got[i] < need[i]) { finish_type_if_known(i); did_any = true; break; } } if (did_any) continue; // If still stuck, just transact at lowerSafe once (it’s valid), // but **guard** to ensure no capacity overflow in the actual result. auto [L, R] = transaction(lowerSafe); bool real_ok = true; for (int x : L) if (got[x] + 1 > need[x]) real_ok = false; if (!real_ok) { // Should not happen under constraints/logic above. // To be safe, do a known price if any (even if none earlier). // Find the smallest i where all candidates have the same P[i]. recompute_agreed(); bool did = false; for (int i = 1; i < N; ++i) { if (knownP[i] != -1 && got[i] < need[i]) { finish_type_if_known(i); did = true; break; } } if (!did) { // As absolute fallback (shouldn’t be reached), pick the smallest i that still has room // and use the *minimum* possible price among candidates for that i (guaranteed >= Pmin) // so M will be valid. This preserves safety; it might not discriminate candidates well, // but we’ll keep moving and stay within capacities. int target = -1; for (int i = 1; i < N; ++i) if (got[i] < need[i]) { target = i; break; } long long m_guess = LLONG_MAX; for (auto &Pc : candidates) m_guess = min(m_guess, Pc[target]); auto [L2, R2] = transaction(m_guess); for (int x : L2) got[x]++; // filter candidates vector<vector<long long>> next; for (auto &Pc : candidates) { auto [LL, RR] = simulate_with_P(Pc, m_guess); if (LL == L2 && RR == R2) next.push_back(Pc); } candidates.swap(next); recompute_agreed(); continue; } continue; } else { for (int x : L) got[x]++; // filter candidates by the real observation we just made vector<vector<long long>> next; for (auto &Pc : candidates) { auto [LL, RR] = simulate_with_P(Pc, lowerSafe); if (LL == L && RR == R) next.push_back(Pc); } candidates.swap(next); recompute_agreed(); continue; } } // Perform the chosen bestM, update counts, and filter candidates auto [Lreal, Rreal] = transaction(bestM); for (int x : Lreal) got[x]++; vector<vector<long long>> next; for (auto &Pc : candidates) { auto [LL, RR] = simulate_with_P(Pc, bestM); if (LL == Lreal && RR == Rreal) next.push_back(Pc); } candidates.swap(next); recompute_agreed(); } // If anything left (shouldn’t be), finish with known prices for (int i = 1; i < N; ++i) { while (got[i] < need[i]) { long long M = knownP[i]; if (M == -1) { // if somehow unknown, pick it from the (now tiny) candidate set M = candidates[0][i]; knownP[i] = M; } auto [L, R] = transaction(M); (void)R; for (int x : L) got[x]++; } } }
#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...