// 8割ぐらい自分で考察して細かい部分と実装は GPT 5.2 thinking と Gemini 3.0 Pro (High) がやりました // Main.java import java.io.*; import java.util.*; /** * Parallel Hit & Blow (interactive) solver. * * Public API: * - interface Oracle * - static class Solver * * Local tests can implement Oracle and call: * Solver solver = new Solver(seed); * solver.selector.setParams(...); // optional * int queries = solver.run(oracle); * * For yukicoder submission, main() uses StdIOOracle (interactive). */ public class Main { // ---------- Fast Scanner (interactive) ---------- private static final class FastScanner { private final InputStream in; private final byte[] buffer = new byte[1 << 16]; private int ptr = 0, len = 0; FastScanner(InputStream in) { this.in = in; } private int readByte() throws IOException { if (ptr >= len) { len = in.read(buffer); ptr = 0; if (len <= 0) return -1; } return buffer[ptr++]; } int nextInt() throws IOException { int c; do { c = readByte(); if (c == -1) return Integer.MIN_VALUE; } while (c <= ' '); int sign = 1; if (c == '-') { sign = -1; c = readByte(); } int val = 0; while (c > ' ') { val = val * 10 + (c - '0'); c = readByte(); } return val * sign; } } // ---------- Code space (10P5) ---------- public static final class CodeSpace { public static final int N = 30240; public static final int RESP_BINS = 36; // id = 6*H + B public final int[] pack = new int[N]; // 5 nibbles packed in 20 bits public final short[] mask = new short[N]; // 10-bit digit set mask public final byte[] d0 = new byte[N]; public final byte[] d1 = new byte[N]; public final byte[] d2 = new byte[N]; public final byte[] d3 = new byte[N]; public final byte[] d4 = new byte[N]; // map packed(20-bit) -> codeId (or -1 if not in space) public final int[] packToId = new int[1 << 20]; public CodeSpace() { Arrays.fill(packToId, -1); int idx = 0; 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) { int p = (a) | (b << 4) | (c << 8) | (d << 12) | (e << 16); int m = (1 << a) | (1 << b) | (1 << c) | (1 << d) | (1 << e); pack[idx] = p; mask[idx] = (short) m; d0[idx] = (byte) a; d1[idx] = (byte) b; d2[idx] = (byte) c; d3[idx] = (byte) d; d4[idx] = (byte) e; packToId[p] = idx; idx++; } } } } } if (idx != N) throw new AssertionError("code space size mismatch: " + idx); } public int respId(int sId, int qId) { int hit = 0; if (d0[sId] == d0[qId]) hit++; if (d1[sId] == d1[qId]) hit++; if (d2[sId] == d2[qId]) hit++; if (d3[sId] == d3[qId]) hit++; if (d4[sId] == d4[qId]) hit++; int common = Integer.bitCount((mask[sId] & mask[qId]) & 0x3FF); int blow = common - hit; return hit * 6 + blow; } public String toStringCode(int id) { char[] cs = new char[5]; cs[0] = (char) ('0' + d0[id]); cs[1] = (char) ('0' + d1[id]); cs[2] = (char) ('0' + d2[id]); cs[3] = (char) ('0' + d3[id]); cs[4] = (char) ('0' + d4[id]); return new String(cs); } } // ---------- Response (judge -> solver) ---------- public static final class Response { public final int solvedCount; // # of (5,0) among 30 lines public final boolean invalid; // (-1,-1) signal or EOF public final int remaining; // R = 30 - solvedCount public final int[] c = new int[CodeSpace.RESP_BINS]; // counts for remaining only (exclude (5,0)) public Response(int solvedCount, boolean invalid) { this.solvedCount = solvedCount; this.invalid = invalid; this.remaining = 30 - solvedCount; } } // ---------- Oracle API ---------- public interface Oracle { Response ask(int qId) throws Exception; } // ---------- StdIO Oracle (interactive) ---------- public static final class StdIOOracle implements Oracle { private final FastScanner fs; private final PrintWriter out; private final CodeSpace space; public StdIOOracle(InputStream in, OutputStream out, CodeSpace space) { this.fs = new FastScanner(in); this.out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(out))); this.space = space; } @Override public Response ask(int qId) throws Exception { out.println(space.toStringCode(qId)); out.flush(); int solved = 0; boolean invalid = false; int[] cnt = new int[CodeSpace.RESP_BINS]; // read 30 lines (Hi, Bi). Always consume all lines. for (int i = 0; i < 30; i++) { int h = fs.nextInt(); int b = fs.nextInt(); if (h == Integer.MIN_VALUE || b == Integer.MIN_VALUE) { invalid = true; h = -1; b = -1; } if (h == -1 && b == -1) invalid = true; if (h == 5 && b == 0) { solved++; } else if (!invalid) { int id = h * 6 + b; if (0 <= id && id < CodeSpace.RESP_BINS) cnt[id]++; } } Response r = new Response(solved, invalid); System.arraycopy(cnt, 0, r.c, 0, cnt.length); return r; } } // ---------- Weight model ---------- private static final class WeightModel { final int n; final double[] w; int R; // remaining secrets count WeightModel(int n, int initialR) { this.n = n; this.R = initialR; this.w = new double[n]; double init = (double) initialR / n; Arrays.fill(w, init); } void exclude(int id) { w[id] = 0.0; } int argMaxNotZero(boolean[] asked) { int best = -1; double bestW = -1; for (int i = 0; i < n; i++) { if (asked[i]) continue; double wi = w[i]; if (wi > bestW) { bestW = wi; best = i; } } return best; } double maxNotZero(boolean[] asked) { double bestW = 0; for (int i = 0; i < n; i++) { if (asked[i]) continue; bestW = Math.max(bestW, w[i]); } return bestW; } void normalizeToR() { double sum = 0.0; for (int i = 0; i < n; i++) sum += w[i]; if (sum <= 0) return; double mul = (double) R / sum; for (int i = 0; i < n; i++) w[i] *= mul; } void updateByRegionScaling(CodeSpace space, int qId, int[] c, int newR, double eta) { this.R = newR; double[] W = new double[CodeSpace.RESP_BINS]; for (int s = 0; s < n; s++) { double ws = w[s]; if (ws == 0.0) continue; int rid = space.respId(s, qId); W[rid] += ws; } final double eps = 1e-12; double[] factor = new double[CodeSpace.RESP_BINS]; for (int r = 0; r < CodeSpace.RESP_BINS; r++) { double num = c[r] + eps; double den = W[r] + eps; factor[r] = Math.pow(num / den, eta); } for (int s = 0; s < n; s++) { double ws = w[s]; if (ws == 0.0) continue; int rid = space.respId(s, qId); double nw = ws * factor[rid]; if (nw < 0) nw = 0; if (nw > 5.0) nw = 5.0; w[s] = nw; } normalizeToR(); } } // ---------- Weighted sampler ---------- private static final class Sampler { final int n; final double[] prefix; Sampler(int n) { this.n = n; this.prefix = new double[n]; } void build(double[] w) { double sum = 0.0; for (int i = 0; i < n; i++) { sum += w[i]; prefix[i] = sum; } } int sampleOne(SplittableRandom rng, double total) { double x = rng.nextDouble() * total; int lo = 0, hi = n - 1; while (lo < hi) { int mid = (lo + hi) >>> 1; if (prefix[mid] >= x) hi = mid; else lo = mid + 1; } return lo; } int[] sampleMany(SplittableRandom rng, double[] w, int T) { build(w); int[] s = new int[T]; double total = prefix[n - 1]; if (total <= 0) { for (int i = 0; i < T; i++) s[i] = rng.nextInt(n); return s; } for (int i = 0; i < T; i++) s[i] = sampleOne(rng, total); return s; } } // ---------- Query selection (approx Max-Entropy via Gini on samples) // ---------- public static final class QuerySelector { final CodeSpace space; final WeightModel model; final SplittableRandom rng; // tunables int K = 500; // candidates per step int T = 2048; // weighted samples per step int TOP = 80; // always include top-weight codes double eta = 0.7; // update learning rate double pickThreshold = 0.60; // collection start threshold // stamping to avoid clearing boolean[] each time final int[] seenStamp = new int[CodeSpace.N]; int stamp = 1; QuerySelector(CodeSpace space, WeightModel model, long seed) { this.space = space; this.model = model; this.rng = new SplittableRandom(seed); } public void setParams(int K, int T, int TOP, double eta, double pickThreshold) { if (K > 0) this.K = K; if (T > 0) this.T = T; if (TOP >= 0) this.TOP = TOP; if (eta > 0) this.eta = eta; if (pickThreshold >= 0) this.pickThreshold = pickThreshold; } int select(boolean[] asked) { double mx = model.maxNotZero(asked); if (mx >= pickThreshold || (model.R <= 6 && mx >= 0.35)) { int best = model.argMaxNotZero(asked); if (best != -1) return best; } Sampler sampler = new Sampler(CodeSpace.N); int[] samples = sampler.sampleMany(rng, model.w, T); int[] candidates = buildCandidates(asked); int bestQ = candidates[0]; long bestScore = Long.MAX_VALUE; // minimize sumSq int[] cnt = new int[CodeSpace.RESP_BINS]; for (int qId : candidates) { Arrays.fill(cnt, 0); for (int sId : samples) { int rid = space.respId(sId, qId); cnt[rid]++; } long sumSq = 0; for (int r = 0; r < CodeSpace.RESP_BINS; r++) { long v = cnt[r]; sumSq += v * v; } if (sumSq < bestScore) { bestScore = sumSq; bestQ = qId; } } return bestQ; } private int[] buildCandidates(boolean[] asked) { stamp++; if (stamp == Integer.MAX_VALUE) { Arrays.fill(seenStamp, 0); stamp = 1; } int[] top = topKNotAsked(asked, TOP); int[] cand = new int[K]; int sz = 0; for (int id : top) { if (id < 0) continue; if (asked[id]) continue; if (seenStamp[id] == stamp) continue; seenStamp[id] = stamp; cand[sz++] = id; if (sz >= K) break; } while (sz < K) { int id = rng.nextInt(CodeSpace.N); if (asked[id]) continue; if (model.w[id] == 0.0) continue; if (seenStamp[id] == stamp) continue; seenStamp[id] = stamp; cand[sz++] = id; } if (sz < K) cand = Arrays.copyOf(cand, sz); return cand; } private int[] topKNotAsked(boolean[] asked, int k) { if (k <= 0) return new int[0]; int[] ids = new int[k]; double[] val = new double[k]; Arrays.fill(ids, -1); Arrays.fill(val, -1); for (int i = 0; i < CodeSpace.N; i++) { if (asked[i]) continue; double wi = model.w[i]; if (wi <= val[k - 1]) continue; int pos = k - 1; while (pos > 0 && wi > val[pos - 1]) { val[pos] = val[pos - 1]; ids[pos] = ids[pos - 1]; pos--; } val[pos] = wi; ids[pos] = i; } return ids; } } // ---------- Solver (public API) ---------- public static final class Solver { public final CodeSpace space = new CodeSpace(); private final WeightModel model = new WeightModel(CodeSpace.N, 30); private final boolean[] asked = new boolean[CodeSpace.N]; public final QuerySelector selector; public int queries = 0; public int solvedCount = 0; public Solver(long seed) { this.selector = new QuerySelector(space, model, seed); } /** * Run until completion or oracle invalid. Returns number of queries performed. */ public int run(Oracle oracle) throws Exception { while (true) { int qId = selector.select(asked); if (qId < 0) return queries; asked[qId] = true; model.exclude(qId); // never ask again / never be a remaining secret queries++; Response resp = oracle.ask(qId); if (resp == null || resp.invalid) return queries; if (resp.solvedCount == 30) return queries; int newR = 30 - resp.solvedCount; // Update model using counts for remaining secrets only (exclude (5,0)) model.updateByRegionScaling(space, qId, resp.c, newR, selector.eta); solvedCount = resp.solvedCount; if (queries >= 30240) return queries; } } } // ---------- Interactive entry point ---------- public static void main(String[] args) throws Exception { long seed = 523; Solver solver = new Solver(seed); solver.selector.setParams( 500, // K candidates 2048, // T samples 80, // TOP 1.0, // eta 0.05 // pickThreshold ); StdIOOracle oracle = new StdIOOracle(System.in, System.out, solver.space); solver.run(oracle); System.out.flush(); } }