#include using namespace std; // 10P5 = 30240 candidates static constexpr int R = 36; // id = h*6 + b static constexpr int ID_50 = 5 * 6 + 0; // (5,0) struct Cand { array d; // digits uint16_t mask; // 10-bit mask string s; // "01234" }; struct Constraint { int q_idx; // which query vector resp; // resp[i] = id(candidate i vs q) array cnt; // histogram for current remaining secrets }; int main() { // ios::sync_with_stdio(false); // cin.tie(nullptr); // --- Generate all candidates (lexicographic, 30240) --- vector cand; cand.reserve(30240); for (int a = 0; a < 10; a++) { for (int b = 0; b < 10; b++) if (b != a) { for (int c = 0; c < 10; c++) if (c != a && c != b) { for (int d = 0; d < 10; d++) if (d != a && d != b && d != c) { for (int e = 0; e < 10; e++) if (e != a && e != b && e != c && e != d) { Cand x; x.d = {(uint8_t)a,(uint8_t)b,(uint8_t)c,(uint8_t)d,(uint8_t)e}; x.mask = (uint16_t)((1u< w(N, remaining / (double)N); vector asked(N, 0); vector cons; cons.reserve(128); // Time control const double TIME_LIMIT_SEC = 4.90; // easy to tweak const int BASE_IPFP_ITER = 4; // try 4 when time allows (tweakable) const auto t_start = chrono::steady_clock::now(); auto elapsed_sec = [&]() -> double { auto now = chrono::steady_clock::now(); return chrono::duration(now - t_start).count(); }; // Build resp array for a query q_idx: resp[i] = (h*6+b) between candidate i and q auto build_resp = [&](int q_idx) -> vector { vector resp(N); const auto &q = cand[q_idx]; const uint16_t qmask = q.mask; const auto &qd = q.d; for (int i = 0; i < N; i++) { const auto &sd = cand[i].d; int h = (sd[0] == qd[0]) + (sd[1] == qd[1]) + (sd[2] == qd[2]) + (sd[3] == qd[3]) + (sd[4] == qd[4]); int common = __builtin_popcount((unsigned)(cand[i].mask & qmask)); int b = common - h; resp[i] = (uint8_t)(h * 6 + b); } return resp; }; // IPFP update: adjust weights so that for each constraint, expected histogram matches cnt auto ipfp_update = [&](int iters) { if (cons.empty()) return; for (int it = 0; it < iters; it++) { for (auto &c : cons) { double E[R]; for (int r = 0; r < R; r++) E[r] = 0.0; // expected counts const auto &resp = c.resp; for (int i = 0; i < N; i++) { double wi = w[i]; if (wi == 0.0) continue; E[resp[i]] += wi; } // scaling factors double f[R]; for (int r = 0; r < R; r++) { if (c.cnt[r] == 0) f[r] = 0.0; else if (E[r] > 0.0) { double val = (double)c.cnt[r] / E[r]; // mild clamp for numerical safety if (val > 1e9) val = 1e9; f[r] = val; } else { // Should not happen if constraints are consistent, // but keep safe. f[r] = 0.0; } } // apply scaling for (int i = 0; i < N; i++) { double wi = w[i]; if (wi == 0.0) continue; w[i] = wi * f[resp[i]]; } } // normalize sum(w)=remaining double sum = 0.0; for (double wi : w) sum += wi; if (!(sum > 0.0) || !isfinite(sum)) { // fallback: uniform over unasked int cnt_unasked = 0; for (int i = 0; i < N; i++) if (!asked[i]) cnt_unasked++; if (cnt_unasked == 0) return; double uni = remaining / (double)cnt_unasked; for (int i = 0; i < N; i++) w[i] = asked[i] ? 0.0 : uni; } else { double scale = remaining / sum; for (double &wi : w) wi *= scale; } } }; // --- Main interactive loop --- while (true) { // choose next query = argmax weight among unasked int q_idx = -1; double best = -1.0; for (int i = 0; i < N; i++) { if (asked[i]) continue; if (w[i] > best) { best = w[i]; q_idx = i; } } if (q_idx < 0) return 0; // should not happen asked[q_idx] = 1; // output query cout << cand[q_idx].s << "\n" << flush; // read response (30 pairs) array total{}; total.fill(0); vector> hb(30); for (int i = 0; i < 30; i++) { int h, b; if (!(cin >> h >> b)) return 0; hb[i] = {h, b}; if (h == -1 && b == -1) { // invalid interaction -> terminate immediately return 0; } int id = h * 6 + b; if (0 <= id && id < R) total[id]++; } // if (H0,B0)=(5,0) then all are solved (because sorted) if (hb[0].first == 5) return 0; const int solved_before = solved; const int total50 = total[ID_50]; // detect if this query hit a NEW secret (count of (5,0) increases by 1) const bool hit_new = (total50 > solved_before); // build resp array for this query (needed for constraints/IPFP) vector resp = build_resp(q_idx); if (hit_new) { solved++; remaining--; // this solved string is removed from all previous constraints: // cnt[resp_of_solved]-- for (auto &c : cons) { uint8_t r = c.resp[q_idx]; c.cnt[r]--; } } // counts for remaining secrets after this query array cnt = total; // subtract already-solved-before-query contributions to (5,0) cnt[ID_50] -= (int16_t)solved_before; // if we newly hit, subtract this q itself from remaining set if (hit_new) cnt[ID_50]--; // queried string can never be "remaining" any more w[q_idx] = 0.0; // add new constraint Constraint nc; nc.q_idx = q_idx; nc.resp = std::move(resp); nc.cnt = cnt; cons.push_back(std::move(nc)); // decide IPFP iterations by remaining time double left = TIME_LIMIT_SEC - elapsed_sec(); int iters = BASE_IPFP_ITER; if (left < 0.40) iters = 1; else if (left < 1.00) iters = min(iters, 2); else if (left < 2.00) iters = min(iters, 3); ipfp_update(iters); } }