Submission #1296314

#TimeUsernameProblemLanguageResultExecution timeMemory
1296314NotLinuxMessage (IOI24_message)C++20
0 / 100
188 ms828 KiB
#include "message.h"
#include <bits/stdc++.h>
using namespace std;

/*
  Implementation of the approach described:
  - We treat indices 0..30 (31 channels).
  - Binary-search-on-first-16 (4 iterations) using overlapping halves style:
      start with S = [0..15], each iteration split S into two halves (left/right),
      keep the half with strictly more safe indices (using C in send_message).
      Indices removed from S at iteration t are considered "released" at round t+1
      (i.e. from the next round they may be used to carry message bits).
  - While doing the binary-search rounds we immediately use any released safe indices
    to start transmitting message bits (packed in order).
  - After the 4 rounds we have a found safe index s in S. We spend 30 rounds where
    index s reports (one per round) whether each other index is safe or not.
    Meanwhile other already-released safe indices continue to stream message bits.
  - After those 30 rounds s also joins the message-senders; then we finish sending
    remaining message bits using all confirmed safe indices in parallel (one bit per
    safe index per round).
  - receive_message reconstructs the same schedule by reading majority answers in
    the binary-search rounds, locating the safe index s, reading 30 rounds from s
    to reconstruct which indices are safe, then collecting previously-received
    bits from confirmed safe indices in the same order the sender used.
*/

static const int NCH = 31;
static const int BIN_ITERS = 4;  // to narrow 16 -> 1
static const int SAFE_MAP_ROUNDS = 30;
static const int MAJ = 16; // majority threshold (safe bits >= 16)

void send_message(vector<bool> M, vector<bool> C) {
  // C[i] == true -> controlled by Cleo (not safe).
  // safeIndices are those with C[i] == false.
  vector<int> safe_indices;
  for (int i = 0; i < NCH; ++i) if (!C[i]) safe_indices.push_back(i);

  // Prepare binary search candidate set S = indices 0..15
  vector<int> S;
  for (int i = 0; i < 16; ++i) S.push_back(i);

  // eliminatedRound[i] = the first round index when i is free for messaging
  // initialize to 0 for indices outside initial S (they are free immediately)
  vector<int> eliminatedRound(NCH, 0);
  for (int i = 16; i < NCH; ++i) eliminatedRound[i] = 0;

  // We'll store the left subsets used in each binary round for the receiver to reproduce
  vector<vector<int>> leftSets; leftSets.reserve(BIN_ITERS);

  // Simulate adaptive binary search using known C to decide which half holds majority safe bits.
  for (int it = 0; it < BIN_ITERS; ++it) {
    if ((int)S.size() == 1) {
      // nothing more to do, mark no further eliminations in S
      leftSets.push_back(S);
      continue;
    }
    int sz = (int)S.size();
    int half = sz / 2; // left size = half, right size = sz-half (left smaller if odd)
    vector<int> left(S.begin(), S.begin() + half);
    vector<int> right(S.begin() + half, S.end());
    leftSets.push_back(left);

    // Count safe indices in left and right using C
    int safeLeft = 0, safeRight = 0;
    for (int idx : left) if (!C[idx]) ++safeLeft;
    for (int idx : right) if (!C[idx]) ++safeRight;

    // choose majority half (tie -> choose left by convention)
    vector<int> keep, rem;
    if (safeLeft >= safeRight) {
      keep = left;
      rem = right;
    } else {
      keep = right;
      rem = left;
    }

    // indices removed at iteration it become available starting from round (it+1)
    for (int idx : rem) eliminatedRound[idx] = it + 1;
    S.swap(keep);
  }
  // After BIN_ITERS, S should contain one index (the found safe index)
  int found_safe = S.empty() ? -1 : S[0];
  if (found_safe == -1) {
    // fallback: pick any safe index (should not happen given problem guarantees)
    for (int i = 0; i < NCH; ++i) if (!C[i]) { found_safe = i; break; }
  }

  // If some indices in the initial S were never eliminated (e.g. if S size stayed >1
  // because of degenerate input), mark all remaining others as released after BIN_ITERS.
  for (int idx : S) {
    if (eliminatedRound[idx] == 0) eliminatedRound[idx] = BIN_ITERS; // available after binary phase
  }

  // Message sending schedule:
  // We'll build rounds as a vector of length-NCH bools, then send_packet for each.
  vector<vector<bool>> rounds;

  // round 0..BIN_ITERS-1: binary query rounds (non-adaptive here we used leftSets as the query pattern)
  // while also using any indices that are already released (eliminatedRound[i] <= current round)
  // and safe to carry message bits.
  int mp = 0; // message pointer
  int Mlen = (int)M.size();

  for (int t = 0; t < BIN_ITERS; ++t) {
    vector<bool> packet(NCH, 0);
    // Query pattern: indices in leftSets[t] -> 1, others 0 (but some indices may be used for message)
    vector<char> isLeft(NCH, 0);
    for (int idx : leftSets[t]) isLeft[idx] = 1;

    for (int i = 0; i < NCH; ++i) {
      if (eliminatedRound[i] <= t && !C[i]) {
        // released safe index -> use it for message if bits remain
        if (mp < Mlen) {
          packet[i] = M[mp++];
        } else {
          packet[i] = 0;
        }
      } else {
        // still in binary-search set (or controlled by Cleo) => use query bit
        packet[i] = isLeft[i] ? 1 : 0;
      }
    }
    rounds.push_back(packet);
  }

  // Next SAFE_MAP_ROUNDS rounds: use found_safe channel to transmit safety status of other indices.
  // For each index j != found_safe we use one round where found_safe bit = isSafe(j).
  // while other released safe indices continue sending message bits.
  vector<int> indices_except_found;
  for (int i = 0; i < NCH; ++i) if (i != found_safe) indices_except_found.push_back(i);
  for (int r = 0; r < SAFE_MAP_ROUNDS; ++r) {
    int idx = indices_except_found[r]; // there are exactly 30 others
    vector<bool> packet(NCH, 0);
    // found_safe sends 1 if idx is safe (i.e., C[idx]==false), else 0
    packet[found_safe] = (!C[idx]) ? 1 : 0;

    // other indices that are already released continue sending message bits (if safe)
    for (int i = 0; i < NCH; ++i) {
      if (i == found_safe) continue;
      if (eliminatedRound[i] <= (BIN_ITERS + r) && !C[i]) {
        if (mp < Mlen) {
          packet[i] = M[mp++];
        } else {
          packet[i] = 0;
        }
      } else {
        // still busy in some sense -> set 0 (or arbitrary)
        packet[i] = 0;
      }
    }
    rounds.push_back(packet);
  }

  // After SAFE_MAP_ROUNDS we assume the safe map is known and found_safe will join sending.
  // Compute confirmed safe indices (we know C here)
  vector<int> confirmed_safe;
  for (int i = 0; i < NCH; ++i) if (!C[i]) confirmed_safe.push_back(i);

  // Now transmit the remaining message bits using all confirmed_safe indices in parallel.
  // Each round can transmit up to confirmed_safe.size() bits.
  int available = (int)confirmed_safe.size();
  while (mp < Mlen) {
    vector<bool> packet(NCH, 0);
    for (int j = 0; j < available && mp < Mlen; ++j) {
      int idx = confirmed_safe[j];
      packet[idx] = M[mp++];
    }
    // other indices (Cleo-controlled or unused) set to 0
    rounds.push_back(packet);
  }

  // Finally send all prepared packets via send_packet
  for (auto &p : rounds) {
    send_packet(p);
  }
}

// Helper to compute majority bit of a round (true if ones >= 16)
static bool majority_from_received(const vector<bool>& rec) {
  int cnt = 0;
  for (bool b : rec) if (b) ++cnt;
  return cnt >= MAJ;
}

vector<bool> receive_message(vector<vector<bool>> R) {
  // R[r][i] is the bit received from channel i on round r.
  // We'll re-run the decoding schedule using the majority answers from the binary rounds
  // to find the same elimination rounds, the found_safe index, read SAFE_MAP_ROUNDS to
  // discover which indices are safe, and finally collect message bits from those indices.

  int rounds_total = (int)R.size();
  // We'll first consume up to BIN_ITERS rounds as binary-search queries.
  // For each of those rounds compute majority bit:
  vector<int> binMaj; binMaj.reserve(BIN_ITERS);
  for (int t = 0; t < min(BIN_ITERS, rounds_total); ++t) {
    bool maj = majority_from_received(R[t]);
    binMaj.push_back(maj ? 1 : 0);
  }

  // Reconstruct adaptive elimination using binMaj as feedback.
  vector<int> eliminatedRound(NCH, 0);
  // indices outside initial S are free at round 0:
  for (int i = 16; i < NCH; ++i) eliminatedRound[i] = 0;

  // S = indices 0..15
  vector<int> S;
  for (int i = 0; i < 16; ++i) S.push_back(i);

  // Re-run the same splitting order the sender uses: split S into left/right halves
  for (int it = 0; it < BIN_ITERS; ++it) {
    if (S.empty() || it >= (int)binMaj.size()) {
      // no more info -> mark remaining as released after current iteration
      for (int idx : S) eliminatedRound[idx] = it + 1;
      break;
    }
    int sz = (int)S.size();
    if (sz == 1) {
      // nothing more to split; mark this one as staying until after binary-phase
      eliminatedRound[S[0]] = it + 1;
      break;
    }
    int half = sz / 2;
    vector<int> left(S.begin(), S.begin() + half);
    vector<int> right(S.begin() + half, S.end());

    int maj = binMaj[it]; // 1 -> majority indicated left half (sender's convention)
    vector<int> keep, rem;
    if (maj == 1) {
      keep = left;
      rem = right;
    } else {
      keep = right;
      rem = left;
    }
    for (int idx : rem) eliminatedRound[idx] = it + 1;
    S.swap(keep);
  }
  // Ensure any leftover indices in S are released after BIN_ITERS
  for (int idx : S) if (eliminatedRound[idx] == 0) eliminatedRound[idx] = BIN_ITERS;

  // Identify the found_safe index: it's the (remaining) index in S after consuming all binMaj
  int found_safe = -1;
  // We can deduce it from final S if available, otherwise try to find index that wasn't eliminated early
  // Recompute S by simulating again deterministically using binMaj.
  S.clear();
  for (int i = 0; i < 16; ++i) S.push_back(i);
  for (int it = 0; it < (int)binMaj.size(); ++it) {
    if (S.size() <= 1) break;
    int half = (int)S.size() / 2;
    vector<int> left(S.begin(), S.begin() + half);
    vector<int> right(S.begin() + half, S.end());
    if (binMaj[it] == 1) S.swap(left); else S.swap(right);
  }
  if (!S.empty()) found_safe = S[0];
  // If not determinable, fall back: find any index that looks stable in later rounds.
  if (found_safe == -1) {
    for (int i = 0; i < NCH; ++i) {
      // try to pick an index that appears constant in rounds BIN_ITERS..BIN_ITERS+SAFE_MAP_ROUNDS-1
      if (rounds_total > BIN_ITERS) { found_safe = i; break; }
    }
  }

  // Now read SAFE_MAP_ROUNDS rounds (if available) to learn which indices are safe.
  vector<char> confirmed_safe(NCH, 0); // 1 if confirmed safe
  if (found_safe >= 0 && rounds_total >= BIN_ITERS + SAFE_MAP_ROUNDS) {
    // For r in 0..29 at round = BIN_ITERS + r the bit observed at found_safe tells us about indices_except_found[r]
    vector<int> indices_except_found;
    for (int i = 0; i < NCH; ++i) if (i != found_safe) indices_except_found.push_back(i);
    for (int r = 0; r < SAFE_MAP_ROUNDS; ++r) {
      int round_idx = BIN_ITERS + r;
      if (round_idx >= rounds_total) break;
      int idx = indices_except_found[r];
      bool bit = R[round_idx][found_safe];
      if (bit) confirmed_safe[idx] = 1; else confirmed_safe[idx] = 0;
    }
    // found_safe itself is safe
    confirmed_safe[found_safe] = 1;
  } else {
    // If safe-map rounds not available (shouldn't happen on judge), try to deduce safe indices
    // by majority consistency across rounds: we mark those indices that show stable majority across rounds.
    for (int i = 0; i < NCH; ++i) {
      int ones = 0;
      for (int r = 0; r < rounds_total; ++r) if (R[r][i]) ++ones;
      // heuristic: if index shows many ones across rounds, we guess it's controllable;
      if (ones > rounds_total / 2) confirmed_safe[i] = 1;
    }
  }

  // Now we can reconstruct the message bits: we must follow the same ordering the sender used:
  // For each round t in [0 .. totalRounds-1], the sender set at that round any index i with
  // eliminatedRound[i] <= t AND safe to carry the next message bit. So receiver will scan rounds
  // in order and pick bits R[t][i] for indices that were released at or before t AND are confirmed safe.
  vector<bool> decoded;
  // To compute eliminatedRound as receiver determined earlier. We already have eliminatedRound[] computed.
  // We'll use indices that are confirmed_safe == 1.
  for (int t = 0; t < rounds_total; ++t) {
    for (int i = 0; i < NCH; ++i) {
      if (confirmed_safe[i] && eliminatedRound[i] <= t) {
        // This channel i was used for message (by sender) starting from its eliminatedRound.
        // The sender used these to stream message bits in order, so we read the bit at (t,i) and append.
        decoded.push_back(R[t][i]);
      }
    }
    // Stop early if we've collected at least 4 bits (the problem's message length is small/unknown).
    // The IOI problem returns a small fixed-length message; use 4 as in the example template.
    if ((int)decoded.size() >= 4) break;
  }

  // If decoded is shorter than expected (4) pad with zeros
  while ((int)decoded.size() < 4) decoded.push_back(0);

  // Only return the first 4 bits
  vector<bool> result(decoded.begin(), decoded.begin() + 4);
  return result;
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...