Problem: icpc2025_north_america_qualifier_k
Difficulty: medium
Categories: data structures
Time limit: 1000 ms, memory limit: 256 MB

There is a $2 \times 2$ box of treasure hidden somewhere in a $5 \times 5$ grid. The rows are numbered from 1 to 5, as are the columns, with cell $( 1 , 1 )$ being the top left corner of the grid. Your goal is to find the treasure within 5 queries. You can query a grid cell and ask if there is any part of the treasure chest in that grid cell. After at most 5 queries, you must output the upper left coordinate of the treasure box.

![](images/cfa6a6b1da416e71773006f5c5e9e69980a2138d2ce241dc1a2d1d9fc71423eb.jpg)

# Interaction

Your program can make up to 5 queries for the location of the treasure. To make a query, print a new line of the form

$\textit { ? i j }$

where $i$ and $j$ are integers between 1 and 5. You are asking is the cell at the $i ^ { t h }$ row and $j ^ { t h }$ column has a part of the treasure box. After each query, the interactor will respond with a 0 if the cell does not contain any part of the treasure, or a 1 if the cell does.

After determining the correct answer, output the answer on a new line in the form

! i $j$

specifying the upper left coordinate of the treasure box. After outputting the answer, your program must exit. If you attempt to read a response after outputting an answer, you will receive an arbitrary verdict.

Do not forget to flush the output after each query you output.

If the interactor receives any invalid or unexpected input, the interactor will output $- 1$ and then immediately terminate. Your program must cleanly exit in order to receive a correct verdict, otherwise the verdict that it receives may be an arbitrary verdict indicating that your submission is incorrect.

A testing tool is provided in python. Use it as follows (You may have to replace python3 by just python):

python3 testing_tool.py However-you-run-your-program

The tool is provided as-is, and you should feel free to make whatever alterations or augmentations you like to it. The tool attempts to detect and report common errors, but it is not guaranteed that a program that passes the testing tool will be accepted.

<table><tr><td rowspan="2">Read</td><td colspan="3">Sample Interaction 1</td></tr><tr><td></td><td>？33</td><td>Write</td></tr><tr><td>0</td><td></td><td></td><td></td></tr><tr><td></td><td></td><td>？22</td><td></td></tr><tr><td>1</td><td></td><td></td><td></td></tr><tr><td></td><td></td><td>？11</td><td></td></tr><tr><td>0</td><td></td><td></td><td></td></tr><tr><td></td><td></td><td>？13</td><td></td></tr><tr><td>1</td><td></td><td></td><td></td></tr><tr><td></td><td></td><td>！12</td><td></td></tr></table>

Non-adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>

using namespace std;


static long long queries = 0;
static long long query_limit = 5; 



static void log_metrics() {
    try {
        tout << "queries=" << queries << endl;
        tout << "query_limit=" << query_limit << endl;
    } catch (...) {
        
    }
}



static void finish(TResult verdict, const char* fmt, ...) {
    log_metrics();
    
    char buf[4096];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf(buf, sizeof(buf), fmt, ap);
    va_end(ap);
    
    quitf(verdict, "%s", buf);
}



static void die(const char* fmt, ...) {
    cout << -1 << endl;
    
    log_metrics();
    
    char buf[4096];
    va_list ap;
    va_start(ap, fmt);
    vsnprintf(buf, sizeof(buf), fmt, ap);
    va_end(ap);
    
    quitf(_wa, "%s", buf);
}

int main(int argc, char** argv) {
    
    registerInteraction(argc, argv);
    atexit(log_metrics);

    
    
    
    int hidden_r = inf.readInt(1, 4, "hidden_r");
    int hidden_c = inf.readInt(1, 4, "hidden_c");

    
    while (true) {
        
        if (queries > 200) {
            finish(_pe, "Hard query limit exceeded (>200)");
        }

        string token;
        
        if (!(cin >> token)) {
            finish(_wa, "Unexpected EOF (expected query or answer)");
        }

        int r_in = -1, c_in = -1;
        bool is_answer = false;

        
        if (token == "!") {
            is_answer = true;
            if (!(cin >> r_in >> c_in)) {
                finish(_wa, "Incomplete answer coordinates");
            }
        } else if (token == "?") {
            if (!(cin >> r_in >> c_in)) {
                die("Incomplete query coordinates");
            }
        } else {
            
            try {
                size_t pos;
                r_in = stoi(token, &pos);
                if (pos != token.size()) throw exception();
            } catch (...) {
                die("Invalid token '%s' (expected '?', '!' or integer)", token.c_str());
            }
            if (!(cin >> c_in)) {
                die("Incomplete query coordinates (missing column)");
            }
        }

        if (is_answer) {
            
            if (r_in == hidden_r && c_in == hidden_c) {
                finish(_ok, "Correct answer: (%d, %d)", hidden_r, hidden_c);
            } else {
                finish(_wa, "Wrong answer. Expected (%d, %d), found (%d, %d)", hidden_r, hidden_c, r_in, c_in);
            }
        } else {
            
            
            if (r_in < 1 || r_in > 5 || c_in < 1 || c_in > 5) {
                die("Query coordinates (%d, %d) out of bounds [1,5]", r_in, c_in);
            }

            queries++;
            
            log_metrics();

            
            
            bool hit = (r_in >= hidden_r && r_in <= hidden_r + 1) &&
                       (c_in >= hidden_c && c_in <= hidden_c + 1);

            cout << (hit ? 1 : 0) << endl;
        }
    }

    return 0;
}


Adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>

using namespace std;


static long long queries = 0;
static const long long query_limit = 5; 



void log_metrics() {
    try {
        tout << "queries=" << queries << endl;
        tout << "query_limit=" << query_limit << endl;
    } catch (...) {
        
    }
}



void finish(TResult verdict, string msg) {
    log_metrics();
    
    
    if (verdict == _pe) {
        cout << -1 << endl;
    }
    quitf(verdict, "%s", msg.c_str());
}


struct Rect {
    int r, c; 
    
    bool covers(int qr, int qc) const {
        
        return (qr >= r && qr <= r + 1 && qc >= c && qc <= c + 1);
    }
};

int main(int argc, char** argv) {
    
    registerInteraction(argc, argv);
    atexit(log_metrics);

    
    log_metrics();

    
    
    
    while (!inf.seekEof()) {
        inf.readToken();
    }

    
    
    
    vector<Rect> candidates;
    for (int r = 1; r <= 4; ++r) {
        for (int c = 1; c <= 4; ++c) {
            candidates.push_back({r, c});
        }
    }

    
    
    
    int hard_cap = 200; 

    
    while (true) {
        string type;
        
        if (!(cin >> type)) {
            
            finish(_wa, "Unexpected EOF. Expected '?' or '!'");
        }

        if (type == "?") {
            int qr, qc;
            if (!(cin >> qr >> qc)) {
                finish(_pe, "Invalid query format");
            }

            
            if (qr < 1 || qr > 5 || qc < 1 || qc > 5) {
                finish(_pe, format("Query coordinates (%d, %d) out of bounds [1, 5]", qr, qc));
            }

            queries++;
            log_metrics(); 

            if (queries > hard_cap) {
                finish(_pe, "Hard query limit exceeded (infinite loop protection)");
            }

            
            vector<Rect> group0, group1;
            for (const auto& rect : candidates) {
                if (rect.covers(qr, qc)) {
                    group1.push_back(rect);
                } else {
                    group0.push_back(rect);
                }
            }

            
            
            int resp = 0;
            if (group1.size() > group0.size()) {
                resp = 1;
                candidates = group1;
            } else {
                resp = 0;
                candidates = group0;
            }

            
            cout << resp << endl;

        } else if (type == "!") {
            int ans_r, ans_c;
            if (!(cin >> ans_r >> ans_c)) {
                finish(_pe, "Invalid answer format");
            }

            
            if (ans_r < 1 || ans_r > 4 || ans_c < 1 || ans_c > 4) {
                finish(_wa, format("Answer coordinates (%d, %d) out of bounds [1, 4]", ans_r, ans_c));
            }

            
            
            
            
            
            bool possible = false;
            for (const auto& rect : candidates) {
                if (rect.r == ans_r && rect.c == ans_c) {
                    possible = true;
                    break;
                }
            }

            if (!possible) {
                finish(_wa, format("Answer (%d, %d) is inconsistent with query history", ans_r, ans_c));
            } else if (candidates.size() > 1) {
                finish(_wa, format("Solution not unique (%d candidates remain). Contestant guessed.", (int)candidates.size()));
            } else {
                finish(_ok, format("Correct! Found treasure at (%d, %d)", ans_r, ans_c));
            }

        } else {
            finish(_pe, format("Unknown token '%s'. Expected '?' or '!'", type.c_str()));
        }
    }

    return 0;
}


Test generator (C++):
#include "testlib.h"
#include <bits/stdc++.h>

using namespace std;

int main(int argc, char* argv[]) {
    
    registerGen(argc, argv, 1);
    
    
    string mode = opt<string>("mode", "non");

    if (mode == "non") {
        
        
        
        
        
        int s = atoi(argv[1]);
        int r, c;
        
        
        if (s >= 1 && s <= 16) {
            int idx = s - 1;
            r = 1 + (idx / 4);
            c = 1 + (idx % 4);
        } else {
            
            r = rnd.next(1, 4);
            c = rnd.next(1, 4);
        }
        
        
        println(r, c);
        
    } else {
        
        
        long long adaptive_seed = rnd.next(1LL, 4000000000000000000LL);
        println(adaptive_seed);
    }

    return 0;
}