Submission #1342046

#TimeUsernameProblemLanguageResultExecution timeMemory
1342046mehrzadFestival (IOI25_festival)C++17
100 / 100
325 ms154188 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...