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

There are $N$ insects, indexed from $0$ to $N - 1$, running around Pak Blangkon's house. Each insect has a type, which is an integer between $0$ and $10^9$ inclusive. Multiple insects may have the same type.

Suppose insects are grouped by type. We define the cardinality of the most frequent insect type as the number of insects in a group with the most number of insects. Similarly, the cardinality of the rarest insect type is the number of insects in a group with the least number of insects.

For example, suppose that there are $11$ insects, whose types are $[5, 7, 9, 11, 11, 5, 0, 11, 9, 100, 9]$. In this case, the cardinality of the most frequent insect type is $3$. The groups with the most number of insects are type $9$ and type $11$, each consisting of $3$ insects. The cardinality of the rarest insect type is $1$. The groups with the least number of insects are type $7$, type $0$, and type $100$, each consisting of $1$ insect.

Pak Blangkon does not know the type of any insect. He has a machine with a single button that can provide some information about the types of the insects. Initially, the machine is empty. To use the machine, three types of operations can be performed:

1. Move an insect to inside the machine.
2. Move an insect to outside the machine.
3. Press the button on the machine.

Each type of operation can be performed at most $40\;000$ times.

Whenever the button is pressed, the machine reports the cardinality of the most frequent insect type, considering only insects inside the machine.

Your task is to determine the cardinality of the rarest insect type among all $N$ insects in Pak Blangkon's house by using the machine. Additionally, in some subtasks, your score depends on the maximum number of operations of a given type that are performed (see Subtasks section for details).

Implementation Details

You should implement the following procedure:

* int min\_cardinality(int N)
  + $N$: the number of insects.
  + This procedure should return the cardinality of the rarest insect type among all $N$ insects in Pak Blangkon's house.
  + This procedure is called exactly once.The above procedure can make calls to the following procedures:
* void move\_inside(int i)
  + $i$: the index of the insect to be moved inside the machine. The value of $i$ must be between $0$ and $N - 1$ inclusive.
  + If this insect is already inside the machine, the call has no effect on the set of insects in the machine. However, it is still counted as a separate call.
  + This procedure can be called at most $40\;000$ times.
* void move\_outside(int i)
  + $i$: the index of the insect to be moved outside the machine. The value of $i$ must be between $0$ and $N - 1$ inclusive.
  + If this insect is already outside the machine, the call has no effect on the set of insects in the machine. However, it is still counted as a separate call.
  + This procedure can be called at most $40\;000$ times.
* int press\_button()
  + This procedure returns the cardinality of the most frequent insect type, considering only insects inside the machine.
  + This procedure can be called at most $40\;000$ times.
  + The grader is not adaptive. That is, the types of all $N$ insects are fixed before min\_cardinality is called.

Interaction

Let $T$ be an array of $N$ integers where $T[i]$ is the type of insect $i$.

The sample grader reads the input in the following format:

* line $1$: $N$ ($2 \le N \le 2000$)
* line $2$: $T[0] \; T[1] \; \ldots \; T[N - 1]$

If the sample grader detects a protocol violation, the output of the sample grader is Protocol Violation: <MSG>, where <MSG> is one of the following:

* invalid parameter: in a call to move\_inside or move\_outside, the value of $i$ is not between $0$ and $N - 1$ inclusive.
* too many calls: the number of calls to any of move\_inside, move\_outside, or press\_button exceeds $40\;000$.

Otherwise, the output of the sample grader is in the following format:

* line $1$: the return value of min\_cardinality
* line $2$: $q$

Scoring

|  |  |  |
| --- | --- | --- |
| Subtask | Points | Additional Input Constraints |
| 1 | 10 | $N \le 200$ |
| 2 | 15 | $N \le 1000$ |
| 3 | 75 | No additional constraints |

If in any of the test cases, the calls to the procedures move\_inside, move\_outside, or press\_button do not conform to the constraints described in Implementation Details, or the return value of min\_cardinality is incorrect, the score of your solution for that subtask will be $0$.

Let $q$ be the maximum of the following three values: the number of calls to move\_inside, the number of calls to move\_outside, and the number of calls to press\_button.

In subtask 3, you can obtain a partial score. Let $m$ be the maximum value of $\frac{q}{N}$ across all test cases in this subtask. Your score for this subtask is calculated according to the following table:

|  |  |
| --- | --- |
| Condition | Points |
| $20 \textless m$ | $0$ (reported as | quot;Output isn't correct | quot; in CMS) |
| $6 \textless m \le 20$ | $\frac{225}{m - 2}$ |
| $3 \textless m \le 6$ | $81 - \frac{2}{3} m^2$ |
| $m \le 3$ | $75$ |

Note

Consider a scenario in which there are $6$ insects of types $[5, 8, 9, 5, 9, 9]$ respectively. The procedure min\_cardinality is called in the following way:

min\_cardinality(6)

The procedure may call move\_inside, move\_outside, and press\_button as follows.

|  |  |  |  |
| --- | --- | --- | --- |
| Call | Return value | Insects in the machine | Types of insects in the machine |
|  |  | $\{\}$ | $[]$ |
| move\_inside(0) |  | $\{0\}$ | $[5]$ |
| press\_button() | $1$ | $\{0\}$ | $[5]$ |
| move\_inside(1) |  | $\{0, 1\}$ | $[5, 8]$ |
| press\_button() | $1$ | $\{0, 1\}$ | $[5, 8]$ |
| move\_inside(3) |  | $\{0, 1, 3\}$ | $[5, 8, 5]$ |
| press\_button() | $2$ | $\{0, 1, 3\}$ | $[5, 8, 5]$ |
| move\_inside(2) |  | $\{0, 1, 2, 3\}$ | $[5, 8, 9, 5]$ |
| move\_inside(4) |  | $\{0, 1, 2, 3, 4\}$ | $[5, 8, 9, 5, 9]$ |
| move\_inside(5) |  | $\{0, 1, 2, 3, 4, 5\}$ | $[5, 8, 9, 5, 9, 9]$ |
| press\_button() | $3$ | $\{0, 1, 2, 3, 4, 5\}$ | $[5, 8, 9, 5, 9, 9]$ |
| move\_inside(5) |  | $\{0, 1, 2, 3, 4, 5\}$ | $[5, 8, 9, 5, 9, 9]$ |
| press\_button() | $3$ | $\{0, 1, 2, 3, 4, 5\}$ | $[5, 8, 9, 5, 9, 9]$ |
| move\_outside(5) |  | $\{0, 1, 2, 3, 4\}$ | $[5, 8, 9, 5, 9]$ |
| press\_button() | $2$ | $\{0, 1, 2, 3, 4\}$ | $[5, 8, 9, 5, 9]$ |

At this point, there is sufficient information to conclude that the cardinality of the rarest insect type is $1$. Therefore, the procedure min\_cardinality should return $1$.

In this example, move\_inside is called $7$ times, move\_outside is called $1$ time, and press\_button is called $6$ times.

Interactor / grader files:
manager.cpp: #include "testlib.h"

#include <cassert>
#include <csignal>
#include <cstdio>

#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include <map>
#include <set>

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);
  }
  return file;
}

inline double roundToTwoDp(double score) {
  return round(score * 100) / 100;
}

static inline constexpr int kMaxQueries = 40000;

// Insect types are compressed to colors in the range [0, N).
static std::vector<int> color;
static std::vector<bool> in_box;

static std::vector<int> color_occurrences;
static std::multiset<int> max_occurrences;

static std::vector<int> op_counter(3, 0);

int main(int argc, char *argv[]) {
  testlibMode = _checker;
  ouf.mode = _output;

  if (argc < 3) {
    quit(_fail, "Insufficient number of args for manager of 'insects'");
  }

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

  // Must be in this order
  FILE *fout = openFile(argv[2], "a");
  FILE *fin = openFile(argv[1], "r");

  int N;
  int is_partial;
  assert(2 == scanf("%d %d", &N, &is_partial));
  color.resize(N);
  in_box.assign(N, false);

  std::map<int, int> type_to_color;
  for (int i = 0; i < N; ++i) {
    int Ti;
    assert(1 == scanf("%d", &Ti));
    if (type_to_color.find(Ti) == type_to_color.end()) {
      int new_color = type_to_color.size();
      type_to_color[Ti] = new_color;
      max_occurrences.insert(0);
    }
    color[i] = type_to_color[Ti];
  }

  color_occurrences.assign(type_to_color.size(), 0);

  fprintf(fout, "%d\n", N);
  fflush(fout);

  while (true) {
    {
      std::string in_secret = "8";
      char secret[100];
      if (fscanf(fin, "%5s", secret) != 1) {
        quit(_sv, "Could not read secret (possibly, an unexpected termination"
                  " of the program)");
      }
      if (std::string(secret) != in_secret) {
        quit(_sv, "Secret mismatch (possible tampering with the output)");
      }
    }

    int op;
    if (fscanf(fin, "%d", &op) != 1) {
      quit(_fail, "Could not read op.");
    }

    if (op == 0 || op == 1) {
      ++op_counter[op];
      if (op_counter[op] > kMaxQueries) {
        quit(_sv, "Too many queries.");
      }

      int i;
      if (fscanf(fin, "%d", &i) != 1) {
        quit(_fail, "Could not read query parameters.");
      }
      if (i < 0 || i >= N) {
        quit(_sv, "Invalid query parameters.");
      }

      if (op == 0 && !in_box[i]) {
        in_box[i] = true;
        max_occurrences.erase(max_occurrences.find(color_occurrences[color[i]]));
        ++color_occurrences[color[i]];
        max_occurrences.insert(color_occurrences[color[i]]);
      }
      if (op == 1 && in_box[i]) {
        in_box[i] = false;
        max_occurrences.erase(max_occurrences.find(color_occurrences[color[i]]));
        --color_occurrences[color[i]];
        max_occurrences.insert(color_occurrences[color[i]]);
      }
    } else if (op == 2) {
      ++op_counter[2];
      if (op_counter[2] > kMaxQueries) {
        quit(_sv, "Too many queries.");
      }

      int frequency = *(max_occurrences.rbegin());
      fprintf(fout, "%d\n", frequency);
      fflush(fout);
    } else if (op == 3) {
      int answer;
      if (fscanf(fin, "%d", &answer) != 1) {
        quit(_fail, "Could not read answer.");
      }

      int Q = *std::max_element(op_counter.begin(), op_counter.end());

      for (int i = 0; i < N; ++i) {
        if (!in_box[i]) {
          ++color_occurrences[color[i]];
        }
      }
      int correct = N;
      for (int frequency : color_occurrences) {
        correct = std::min(correct, frequency);
      }

      if (answer == correct) {
        if (is_partial == 0) {
          quit(_ok);
        } else if (is_partial == 1) {
          if (Q <= 3 * N) {
            quit(_ok);
          } else if (Q <= 6 * N) {
            quitp(roundToTwoDp(81.0 - pow(Q * 1.0 / N, 2.0) * 2.0 / 3.0) / 75.0);
          } else if (Q <= 20 * N) {
            quitp(roundToTwoDp(225.0 / (Q * 1.0 / N - 2.0)) / 75.0);
          } else {
            quit(_wa, "Too many queries.");
          }
        } else {
          assert(false);
        }
      } else {
        quit(_wa, "Wrong answer.");
      }
    } else {
      quit(_fail, "Invalid value of op.");
    }
  }
}

stub.cpp: #include "insects.h"

#include <cassert>

#include <string>
#include <vector>

static inline constexpr int kMaxQueries = 40000;

static std::vector<int> op_counter;

void move_inside(int i) {
  {
    std::string out_secret = "8";
    printf("%s\n", out_secret.c_str());
  }
  printf("0 %d\n", i);

  ++op_counter[0];
  if (op_counter[0] > kMaxQueries) {
    fflush(stdout);
    exit(0);
  }
}

void move_outside(int i) {
  {
    std::string out_secret = "8";
    printf("%s\n", out_secret.c_str());
  }
  printf("1 %d\n", i);

  ++op_counter[1];
  if (op_counter[1] > kMaxQueries) {
    fflush(stdout);
    exit(0);
  }
}

int press_button() {
  {
    std::string out_secret = "8";
    printf("%s\n", out_secret.c_str());
  }
  printf("2\n");
  fflush(stdout);

  int frequency;
  if (scanf("%d", &frequency) != 1) {
    exit(0);
  }
  return frequency;
}

int main() {
  int N;
  assert(1 == scanf("%d", &N));
  op_counter.resize(2);

  int answer = min_cardinality(N);
  {
    std::string out_secret = "8";
    printf("%s\n", out_secret.c_str());
  }
  printf("3 %d\n", answer);
  fflush(stdout);
  return 0;
}

insects.h: void move_inside(int i);
void move_outside(int i);
int press_button();
int min_cardinality(int N);