제출 #1331708

#제출 시각아이디문제언어결과실행 시간메모리
1331708seannaren선물 (IOI25_souvenirs)C++17
39 / 100
13 ms476 KiB
#include "souvenirs.h"

#include <map>
#include <set>
#include <utility>
#include <vector>

using namespace std;

namespace {

struct Equation {
  bool has = false;
  vector<int> coeff;
  long long rhs = 0;
};

} // namespace

void buy_souvenirs(int N, long long P0) {
  vector<int> cnt(N, 0);
  vector<int> target(N, 0);
  for (int i = 0; i < N; ++i) target[i] = i;

  long long calls = 0;
  bool overshot = false;

  map<long long, vector<int>> known_types;
  vector<Equation> equations(N);
  vector<long long> price(N, -1);
  price[0] = P0;

  auto done = [&]() -> bool {
    for (int i = 1; i < N; ++i) {
      if (cnt[i] != target[i]) return false;
    }
    return true;
  };

  auto safe_known_type = [&](const vector<int> &L) -> bool {
    for (int idx : L) {
      if (cnt[idx] >= target[idx]) return false;
    }
    return true;
  };

  auto do_transaction = [&](long long M) -> pair<vector<int>, long long> {
    pair<vector<int>, long long> res = transaction(M);
    ++calls;

    const vector<int> &L = res.first;
    for (int idx : L) {
      ++cnt[idx];
      if (cnt[idx] > target[idx]) overshot = true;
    }

    if (known_types.find(M) == known_types.end()) {
      known_types[M] = L;
    }

    if (!L.empty()) {
      int first = L.front();
      if (first >= 0 && first < N && !equations[first].has) {
        equations[first].has = true;
        equations[first].rhs = M - res.second;
        equations[first].coeff.assign(N, 0);
        for (int idx : L) equations[first].coeff[idx] = 1;
      }
    }

    return res;
  };

  auto solve_price_closure = [&]() -> bool {
    bool changed = false;
    for (int i = N - 1; i >= 1; --i) {
      if (price[i] != -1) continue;
      if (!equations[i].has) continue;

      long long v = equations[i].rhs;
      bool ready = true;
      for (int j = i + 1; j < N; ++j) {
        if (!equations[i].coeff[j]) continue;
        if (price[j] == -1) {
          ready = false;
          break;
        }
        v -= price[j];
      }
      if (!ready) continue;

      if (v <= 0) continue;
      if (price[i - 1] != -1 && v >= price[i - 1]) continue;
      if (i + 1 < N && price[i + 1] != -1 && v <= price[i + 1]) continue;

      price[i] = v;
      changed = true;
    }
    return changed;
  };

  if (N == 2) {
    if (calls < 5000) do_transaction(P0 - 1);
    return;
  }

  // Phase 1: one descending discovery chain.
  long long M = P0 - 1;
  set<long long> seen;
  const int max_probe = min(400, 4 * N);

  for (int step = 0; step < max_probe; ++step) {
    if (calls >= 5000 || overshot || done()) break;
    if (M <= 0 || M >= P0) break;
    if (seen.find(M) != seen.end()) break;
    seen.insert(M);

    auto res = do_transaction(M);
    const vector<int> &L = res.first;
    long long R = res.second;
    if (L.empty()) break;
    if (L.front() == N - 1 && (int)L.size() == 1) break;

    long long spent = M - R;
    long long nextM;
    if ((int)L.size() == 1) {
      nextM = spent - 1;
    } else {
      long long avg = spent / (long long)L.size();
      if (L.front() == 1) {
        nextM = avg;
      } else {
        nextM = (M + avg) / 2;
      }
    }

    if (nextM >= M) nextM = M - 1;
    if (nextM < 1) nextM = 1;
    M = nextM;
  }

  while (solve_price_closure()) {
  }

  // Phase 2: if P[i-1] is known and row i is missing, query M=P[i-1]-1.
  bool phase2_progress = true;
  while (phase2_progress && calls < 5000 && !overshot && !done()) {
    while (solve_price_closure()) {
    }

    phase2_progress = false;
    int need = -1;
    for (int i = 2; i < N; ++i) {
      if (!equations[i].has && price[i - 1] != -1) {
        need = i;
        break;
      }
    }

    if (need != -1) {
      long long q = price[need - 1] - 1;
      if (q >= 1 && q < P0) {
        do_transaction(q);
        phase2_progress = true;
      }
    }
  }

  while (solve_price_closure()) {
  }

  bool all_prices_known = true;
  for (int i = 1; i < N; ++i) {
    if (price[i] == -1) {
      all_prices_known = false;
      break;
    }
  }

  // Exact singleton completion if prices are known.
  if (all_prices_known && !overshot) {
    for (int i = 1; i < N && calls < 5000 && !overshot; ++i) {
      while (cnt[i] < target[i] && calls < 5000 && !overshot) {
        do_transaction(price[i]);
      }
    }
    return;
  }

  // Fallback: use only previously observed safe transaction types.
  while (calls < 5000 && !overshot && !done()) {
    long long bestM = -1;
    long long bestScore = -1;

    for (const auto &it : known_types) {
      const long long candM = it.first;
      const vector<int> &L = it.second;
      if (!safe_known_type(L)) continue;

      long long score = 0;
      for (int idx : L) score += (long long)(target[idx] - cnt[idx]);
      if (score > bestScore) {
        bestScore = score;
        bestM = candM;
      }
    }

    if (bestM == -1) break;
    do_transaction(bestM);
  }
}
#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...