Problem: ioi25_a
Categories: math, data structures
Difficulty: medium
Time limit: 2000 ms | Memory limit: 2048 MB

Amaru is buying souvenirs in a foreign shop. There are $$$N$$$ types of souvenirs. There are infinitely many souvenirs of each type available in the shop.

Each type of souvenir has a fixed price. Namely, a souvenir of type $$$i$$$ ($$$0 \leq i < N$$$) has a price of $$$P[i]$$$ coins, where $$$P[i]$$$ is a positive integer.

Amaru knows that souvenir types are sorted in decreasing order by price, and that the souvenir prices are distinct. Specifically, $$$P[0] > P[1] > \ldots > P[N-1] > 0$$$. Moreover, he was able to learn the value of $$$P[0]$$$. Unfortunately, Amaru does not have any other information about the prices of the souvenirs.

To buy some souvenirs, Amaru will perform a number of transactions with the seller.

Each transaction consists of the following steps:

1. Amaru hands some (positive) number of coins to the seller.
2. The seller puts these coins in a pile on the table in the back room, where Amaru cannot see them.
3. The seller considers each souvenir type $$$0, 1, \ldots, N-1$$$ in that order, one by one. Each type is considered exactly once per transaction.
   * When considering souvenir type $$$i$$$, if the current number of coins in the pile is at least $$$P[i]$$$, then
     + the seller removes $$$P[i]$$$ coins from the pile, and
     + the seller puts one souvenir of type $$$i$$$ on the table.
4. The seller gives Amaru all the coins remaining in the pile and all souvenirs on the table.

Note that there are no coins or souvenirs on the table before a transaction begins.

Your task is to instruct Amaru to perform some number of transactions, so that:

* in each transaction he buys at least one souvenir, and
* overall he buys exactly $$$i$$$ souvenirs of type $$$i$$$, for each $$$i$$$ such that $$$0 \leq i < N$$$. Note that this means that Amaru should not buy any souvenir of type $$$0$$$.

Amaru does not have to minimize the number of transactions and has an unlimited supply of coins.

Implementation Details

You should implement the following procedure.

void buy\_souvenirs(int N, long long P0)

* $$$N$$$: the number of souvenir types.
* $$$P0$$$: the value of $$$P[0]$$$.
* This procedure is called exactly once for each test case.

The above procedure can make calls to the following procedure to instruct Amaru to perform a transaction:

std::pair<std::vector<int>, long long> transaction(long long M)

* $$$M$$$: the number of coins handed to the seller by Amaru.
* The procedure returns a pair. The first element of the pair is an array $$$L$$$, containing the types of souvenirs that have been bought (in increasing order). The second element is an integer $$$R$$$, the number of coins returned to Amaru after the transaction.
* It is required that $$$P[0] > M \geq P[N-1]$$$. The condition $$$P[0] > M$$$ ensures that Amaru does not buy any souvenir of type $$$0$$$, and $$$M \geq P[N-1]$$$ ensures that Amaru buys at least one souvenir. If these conditions are not met, your solution will receive the verdict Output isn't correct: Invalid argument. Note that contrary to $$$P[0]$$$, the value of $$$P[N-1]$$$ is not provided in the input.
* The procedure can be called at most $$$5000$$$ times in each test case.

The behavior of the grader is not adaptive. This means that the sequence of prices $$$P$$$ is fixed before buy\_souvenirs is called.

Input

The sample grader reads in the input in the following format:

* line $$$1$$$: $$$N$$$ ($$$2 \leq N \leq 100$$$)
* line $$$2$$$: $$$P[0]\ P[1]\ldots \ P[N-1]$$$ ($$$1 \leq P[i] \leq 10^{15}$$$)

$$$P[i] > P[i+1]$$$ for each $$$i$$$ such that $$$0 \leq i < N - 1$$$.

Output

The sample grader prints your answers in the following format:

* line $$$1$$$: $$$Q[0]\ Q[1]\ldots \ Q[N-1]$$$

Here $$$Q[i]$$$ is the number of souvenirs of type $$$i$$$ bought in total for each $$$i$$$ such that $$$0 \leq i < N$$$.

Scoring

|  |  |  |
| --- | --- | --- |
| Subtask | Points | Additional Input Constraints |
| 1 | 4 | $$$N = 2$$$ |
| 2 | 3 | $$$P[i] = N - i$$$ for each $$$i$$$ such that $$$0 \leq i < N$$$. |
| 3 | 14 | $$$P[i] \leq P[i+1] + 2$$$ for each $$$i$$$ such that $$$0 \leq i < N - 1$$$. |
| 4 | 18 | $$$N = 3$$$ |
| 5 | 28 | $$$P[i+1] + P[i+2] \leq P[i]$$$, $$$P[i] \leq 2 \cdot P[i+1]$$$ |
| 6 | 33 | No additional constraints |

Example

Input

```
3
4 3 1
```

Output

```
4 3 1
```

Note

Consider the following call.

buy\_souvenirs(3, 4)

There are $$$N = 3$$$ types of souvenirs and $$$P[0] = 4$$$. Observe that there are only three possible sequences of prices $$$P$$$: $$$[4, 3, 2]$$$, $$$[4, 3, 1]$$$, and $$$[4, 2, 1]$$$.

Assume that buy\_souvenirs calls transaction(2). Suppose the call returns $$$([2], 1)$$$, meaning that Amaru bought one souvenir of type $$$2$$$ and the seller gave him back $$$1$$$ coin. Observe that this allows us to deduce that $$$P = [4, 3, 1]$$$, since:

* For $$$P = [4, 3, 2]$$$, transaction(2) would have returned $$$([2], 0)$$$.
* For $$$P = [4, 2, 1]$$$, transaction(2) would have returned $$$([1], 0)$$$.

Then buy\_souvenirs can call transaction(3), which returns $$$([1], 0)$$$, meaning that Amaru bought one souvenir of type $$$1$$$ and the seller gave him back $$$0$$$ coins. So far, in total, he has bought one souvenir of type $$$1$$$ and one souvenir of type $$$2$$$.

Finally, buy\_souvenirs can call transaction(1), which returns $$$([2], 0)$$$, meaning that Amaru bought one souvenir of type $$$2$$$. Note that we could have also used transaction(2) here. At this point, in total Amaru has one souvenir of type $$$1$$$ and two souvenirs of type $$$2$$$, as required.

Interactor / grader files:
manager.cpp: #include "testlib.h"
#include <cstdio>
#include <csignal>
#include <string>

using namespace std;

/******************************** Begin testlib-related material ********************************/

inline FILE* openFile(const char* name, const char* mode) {
  FILE* file = fopen(name, mode);
  if (!file)
    quitf(_fail, "Could not open file '%s' with mode '%s'.", name, mode);
  closeOnHalt(file);
  return file;
}

vector<FILE*> mgr2sol, sol2mgr;
FILE* log_file = nullptr;

void nullifyFile(int idx) {
  mgr2sol[idx] = sol2mgr[idx] = nullptr;
}

#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
#endif
void
log_printf(const char* fmt, ...) {
  if (log_file) {
    FMT_TO_RESULT(fmt, fmt, message);
    fprintf(log_file, "%s", message.c_str());
    fflush(log_file);
  }
}

void registerManager(std::string probName, int num_processes, int argc, char* argv[]) {
  setName("manager for problem %s", probName.c_str());
  __testlib_ensuresPreconditions();
  testlibMode = _checker;
  random_t::version = 1; // Random generator version
  __testlib_set_binary(stdin);
  ouf.mode = _output;

  { // Keep alive on broken pipes
    // signal(SIGPIPE, SIG_IGN);
    struct sigaction sa;
    sa.sa_handler = SIG_IGN;
    sigaction(SIGPIPE, &sa, NULL);
  }

  int required_args = 1 + 2 * num_processes;
  if (argc < required_args || required_args + 1 < argc) {
    string usage = format("'%s'", argv[0]);
    for (int i = 0; i < num_processes; i++)
      usage += format(" sol%d-to-mgr mgr-to-sol%d", i, i);
    usage += " [mgr_log] < input-file";
    quitf(_fail,
          "Manager for problem %s:\n"
          "Invalid number of arguments: %d\n"
          "Usage: %s",
          probName.c_str(), argc - 1, usage.c_str());
  }

  inf.init(stdin, _input);
  closeOnHalt(stdout);
  closeOnHalt(stderr);

  mgr2sol.resize(num_processes);
  sol2mgr.resize(num_processes);
  for (int i = 0; i < num_processes; i++) {
    mgr2sol[i] = openFile(argv[1 + 2 * i + 1], "a");
    sol2mgr[i] = openFile(argv[1 + 2 * i + 0], "r");
  }

  if (argc > required_args) {
    log_file = openFile(argv[required_args], "w");
  } else {
    log_file = nullptr;
  }
}
/********************************* End testlib-related material *********************************/

// utils

#define rep(i, n) for (int i = 0, i##__n = (int)(n); i < i##__n; ++i)

template <class C>
int sz(const C& c) {
  return int(std::size(c));
}

using LL = long long;

template <class T>
constexpr T div_ceil(T a, T b) {
  return (a + b - 1) / b;
}

void set_vi_bit(vector<int>& v, int i, int b) {
  int& x = v[i / 32];
  int j = i % 32;
  if (b == 1)
    x |= (1 << j);
  else
    x &= ~(1 << j);
}

#define log_var(var_name) log_printf("%s = %s\n", #var_name, toString(var_name).c_str());

template <class C>
void log_array(const C& c, string delim = " ", string ending = "\n") {
  string d = "";
  for (const auto& x : c) {
    log_printf("%s%s", d.c_str(), toString(x).c_str());
    d = delim;
  }
  log_printf("%s", ending.c_str());
}

// grader/manager protocol

const int secret_g2m = 0x729B3F30;
const int secret_m2g = 0x35397FC0;
const int code_mask = 0x0000000F;

const int M2G_CODE__OK = 0;
const int M2G_CODE__DIE = 1;

const int G2M_CODE__OK_NEW_TRANSACTION = 0;
const int G2M_CODE__OK_END_OF_TRANSACTIONS = 1;
const int G2M_CODE__PV_CALL_EXIT = 13;
const int G2M_CODE__TAMPER_M2G = 14;
const int G2M_CODE__SILENT = 15;

int fifo_idx = 0;

enum class ActionMode {
  ok_new_transaction,
  ok_end_of_transactions,
} ok_action_mode;

void out_flush() {
  fflush(mgr2sol[fifo_idx]);
}

void write_int(int x) {
  if (1 != fwrite(&x, sizeof(x), 1, mgr2sol[fifo_idx])) {
    nullifyFile(fifo_idx);
    // add logging here
  }
}

void write_ll(LL x) {
  if (1 != fwrite(&x, sizeof(x), 1, mgr2sol[fifo_idx])) {
    nullifyFile(fifo_idx);
    // add logging here
  }
}

void write_int_vector(const vector<int>& arr) {
  int len = (int)sz(arr);
  if (len != (int)fwrite(arr.data(), sizeof(int), len, mgr2sol[fifo_idx])) {
    nullifyFile(fifo_idx);
    // add logging here
  }
}

void write_secret(int m2g_code = M2G_CODE__OK) {
  write_int(secret_m2g | m2g_code);
}

#ifdef __GNUC__
__attribute__((format(printf, 2, 3)))
#endif
NORETURN void
die(TResult result, const char* format, ...) {
  FMT_TO_RESULT(format, format, message);
  log_printf("Dying with message '%s'\n", message.c_str());
  rep(i, sz(mgr2sol)) if (mgr2sol[i] != nullptr) {
    fifo_idx = i;
    log_printf("Sending secret with code DIE to mgr2sol[%d]\n", fifo_idx);
    write_secret(M2G_CODE__DIE);
    out_flush();
  }
  log_printf("Quitting with result code %d\n", int(result));
  quit(result, message);
}

NORETURN void die_invalid_argument(const string& msg) {
  RESULT_MESSAGE_WRONG += ": Invalid argument";
  die(_wa, "%s", msg.c_str());
}

NORETURN void die_too_many_calls(const string& msg) {
  RESULT_MESSAGE_WRONG += ": Too many calls";
  die(_wa, "%s", msg.c_str());
}

int read_int() {
  int x;
  if (1 != fread(&x, sizeof(x), 1, sol2mgr[fifo_idx])) {
    nullifyFile(fifo_idx);
    die(_fail, "Could not read int from sol2mgr[%d]", fifo_idx);
  }
  return x;
}

LL read_ll() {
  LL x;
  if (1 != fread(&x, sizeof(x), 1, sol2mgr[fifo_idx])) {
    nullifyFile(fifo_idx);
    die(_fail, "Could not read ll from sol2mgr[%d]", fifo_idx);
  }
  return x;
}

void read_int_array(int* arr, int len) {
  if (len != (int)fread(arr, sizeof(*arr), len, sol2mgr[fifo_idx])) {
    nullifyFile(fifo_idx);
    die(_fail, "Could not read int array from sol2mgr[%d]", fifo_idx);
  }
}

void read_secret() {
  int secret = read_int();
  if ((secret & ~code_mask) != secret_g2m)
    die(_pv, "Possible tampering with sol2mgr[%d]", fifo_idx);
  int g2m_code = secret & code_mask;
  switch (g2m_code) {
  case G2M_CODE__OK_NEW_TRANSACTION:
    ok_action_mode = ActionMode::ok_new_transaction;
    return;
  case G2M_CODE__OK_END_OF_TRANSACTIONS:
    ok_action_mode = ActionMode::ok_end_of_transactions;
    return;
  case G2M_CODE__SILENT:
    die(_fail, "Unexpected g2m_code SILENT from sol2mgr[%d]", fifo_idx);
  case G2M_CODE__TAMPER_M2G:
    die(_pv, "Possible tampering with mgr2sol[%d]", fifo_idx);
  case G2M_CODE__PV_CALL_EXIT:
    die(_pv, "Solution[%d] called exit()", fifo_idx);
  default:
    die(_fail, "Unknown g2m_code %d from sol2mgr[%d]", g2m_code, fifo_idx);
  }
}

// problem logic

using Index = int;
using Compressed = int;
using Count = int;
using Money = LL;

#define read_index read_int
#define read_money read_ll
#define write_index write_int
#define write_money write_ll

const bool log_details = false;

const Count TRANSACTIONS_CNT_LIMIT = 5'000;

Index N;

constexpr Compressed compressed_len(Index len) {
  return div_ceil(len, 32);
}

void write_indices(const vector<Index>& X) {
  vector<Compressed> X_f(compressed_len(N), 0);
  for (Index idx : X)
    set_vi_bit(X_f, idx, 1);
  write_int_vector(X_f);
}

int main(int argc, char** argv) {
  registerManager("souvenirs", 1, argc, argv);

  N = inf.readInt();
  vector<Money> P(N);
  rep(i, N)
      P[i] = inf.readLong();
  fclose(stdin);

  log_var(N);
  log_printf("P:\n");
  log_array(P);

  write_secret();
  write_index(N);
  write_money(P[0]);
  out_flush();

  Count transactions_cnt = 0;
  vector<Count> bought(N, 0);

  while (true) {
    log_printf("Checking for new transaction...\n");
    read_secret();
    if (ok_action_mode != ActionMode::ok_new_transaction)
      break;
    log_printf("Received a new transaction\n");

    const Money M = read_money();
    log_var(M);

    if (++transactions_cnt > TRANSACTIONS_CNT_LIMIT)
      die_too_many_calls("Used too many transactions");
    if (M >= P[0])
      die_invalid_argument(format("M (%lld) >= P[0] (%lld)", M, P[0]));
    if (M < P[N - 1])
      die_invalid_argument(format("M (%lld) < P[N-1] (%lld)", M, P[N - 1]));

    vector<Index> L;
    Money R = M;
    rep(idx, N) if (P[idx] <= R) {
      L.push_back(idx);
      R -= P[idx];
    }

    if (log_details) {
      log_printf("L:\n");
      log_array(L);
    } else {
      log_printf("L size=%d\n", sz(L));
    }
    log_var(R);

    ensuref(0 <= R, "negative value of R=%lld", R);
    if (L.empty())
      quitf(_wa, "Nothing is bought with M=%lld", M);
    ensuref(0 <= L[0], "negative index L[%d]=%d", 0, L[0]);
    rep(i, sz(L)) {
      ensuref(L[i] < N, "too large index L[%d]=%d", i, L[i]);
      ensuref(i == 0 || L[i - 1] < L[i], "L is not increasing, L[%d]=%d, L[%d]=%d", i - 1, L[i - 1], i, L[i]);
    }

    for (Index idx : L)
      bought[idx]++;

    log_printf("Sending transaction results...\n");
    write_secret();
    write_indices(L);
    write_money(R);
    out_flush();
    log_printf("Transaction results sent.\n");
  }

  ensuref(ok_action_mode == ActionMode::ok_end_of_transactions, "Expected action code ok_end_of_transactions");
  log_printf("No more transactions.\n");

  // Final checking
  rep(idx, N) if (bought[idx] != idx)
      quitf(_wa, "Item %d is bought %d times, expected %d", idx, bought[idx], idx);
  quitf(_ok, "Finished successfully with %d transaction(s)", transactions_cnt);
}

stub.cpp: #include "souvenirs.h"
#include <cstdio>
#include <cstdlib>
#include <csignal>

using namespace std;

namespace {

/******************************** Begin testlib-related material ********************************/
#ifdef _MSC_VER
#define NORETURN __declspec(noreturn)
#elif defined __GNUC__
#define NORETURN __attribute__((noreturn))
#else
#define NORETURN
#endif
/********************************* End testlib-related material *********************************/

// utils

#define rep(i, n) for (int i = 0, i##__n = (int)(n); i < i##__n; ++i)

template <class C>
int sz(const C& c) {
  return int(std::size(c));
}

using LL = long long;

template <class T>
constexpr T div_ceil(T a, T b) {
  return (a + b - 1) / b;
}

int get_vi_bit(const vector<int>& v, int i) {
  const int& x = v[i / 32];
  int j = i % 32;
  return (x >> j) & 1;
}

// grader/manager protocol

const int secret_g2m = 0x729B3F30;
const int secret_m2g = 0x35397FC0;
const int code_mask = 0x0000000F;

const int M2G_CODE__OK = 0;

const int G2M_CODE__OK_NEW_TRANSACTION = 0;
const int G2M_CODE__OK_END_OF_TRANSACTIONS = 1;
const int G2M_CODE__PV_CALL_EXIT = 13;
const int G2M_CODE__TAMPER_M2G = 14;
const int G2M_CODE__SILENT = 15;

bool exit_allowed = false;

NORETURN void authorized_exit(int exit_code) {
  exit_allowed = true;
  exit(exit_code);
}

FILE* fin = stdin;
FILE* fout = stdout;

void out_flush() {
  fflush(fout);
}

void write_int(int x) {
  if (1 != fwrite(&x, sizeof(x), 1, fout)) {
    fprintf(stderr, "Could not write int to fout\n");
    authorized_exit(3);
  }
}

void write_ll(LL x) {
  if (1 != fwrite(&x, sizeof(x), 1, fout)) {
    fprintf(stderr, "Could not write ll to fout\n");
    authorized_exit(3);
  }
}

void write_secret(int g2m_code) {
  write_int(secret_g2m | g2m_code);
}

NORETURN void die(int g2m_code) {
  if (g2m_code == G2M_CODE__OK_NEW_TRANSACTION || g2m_code == G2M_CODE__OK_END_OF_TRANSACTIONS) {
    fprintf(stderr, "Shall not die with code OK\n");
    authorized_exit(5);
  }
  fprintf(stderr, "Dying with code %d\n", g2m_code);
  if (g2m_code != G2M_CODE__SILENT)
    write_secret(g2m_code);
  fclose(fin);
  fclose(fout);
  authorized_exit(0);
}

int read_int() {
  int x;
  if (1 != fread(&x, sizeof(x), 1, fin)) {
    fprintf(stderr, "Could not read int from fin\n");
    authorized_exit(3);
  }
  return x;
}

LL read_ll() {
  LL x;
  if (1 != fread(&x, sizeof(x), 1, fin)) {
    fprintf(stderr, "Could not read ll from fin\n");
    authorized_exit(3);
  }
  return x;
}

void read_int_array(int* arr, int len) {
  if (len != (int)fread(arr, sizeof(*arr), len, fin)) {
    fprintf(stderr, "Could not read int array from fin\n");
    authorized_exit(3);
  }
}

void read_int_vector(vector<int>& v) {
  read_int_array(v.data(), sz(v));
}

void read_secret() {
  int secret = read_int();
  if ((secret & ~code_mask) != secret_m2g)
    die(G2M_CODE__TAMPER_M2G);
  int m2g_code = secret & code_mask;
  if (m2g_code != M2G_CODE__OK)
    die(G2M_CODE__SILENT);
}

void check_exit_protocol() {
  if (!exit_allowed)
    die(G2M_CODE__PV_CALL_EXIT);
}

// problem logic

using Index = int;
using Compressed = int;
using Count = int;
using Money = LL;

#define read_index read_int
#define read_money read_ll
#define write_index write_int
#define write_money write_ll

Index N;

constexpr Compressed compressed_len(Index len) {
  return div_ceil(len, 32);
}

vector<Index> read_indices() {
  vector<Compressed> X_f(compressed_len(N), 0);
  read_int_vector(X_f);
  vector<Index> X;
  rep(i, N) if (get_vi_bit(X_f, i))
      X.push_back(i);
  return X;
}

} // namespace

pair<vector<Index>, Money> transaction(Money M) {
  write_secret(G2M_CODE__OK_NEW_TRANSACTION);
  write_money(M);
  out_flush();

  read_secret();
  vector<Index> L = read_indices();
  Money R = read_money();
  return make_pair(L, R);
}

int main() {
  signal(SIGPIPE, SIG_IGN);
  atexit(check_exit_protocol);
  at_quick_exit(check_exit_protocol);

  read_secret();
  N = read_index();
  Money P0 = read_money();

  buy_souvenirs(N, P0);

  write_secret(G2M_CODE__OK_END_OF_TRANSACTIONS);
  out_flush();
  authorized_exit(0);
}

souvenirs.h: #include <utility>
#include <vector>

void buy_souvenirs(int N, long long P0);

std::pair<std::vector<int>, long long> transaction(long long M);