Problem: icpc2023_asia_shenyang_l
Difficulty: medium
Categories: search
Time limit: 3000 ms, memory limit: 512 MB

# This is an interactive problem.

Consider an $n \times n$ chessboard, where its rows are numbered 1 to $n$ from top to bottom, and its columns are numbered 1 to $n$ from left to right. The square on the $x$ -th row and the $y$ -th column is denoted as $( x , y )$ and is either empty or occupied by a neutral rook. According to the rules of chess, a rook can move any number of squares along a row or column in one step but cannot leap over other pieces. In this scenario, the control area of a rook consists of the square it occupies and the squares it can reach in one step.

![](images/2329d7eddcb7513f926993d4b549ab7546b1f1ee62c272881a6758dbc073b5ae.jpg)  
Figure 1: The control area of the rook at (3,4)

A square is considered “controlled” if and only if it belongs to one or more control areas. The known information is that all squares are controlled initially, so clearly, there are at least $n$ rooks on the chessboard. Whether each square is empty or occupied by a rook, however, is unknown to you. Your task is to locate at least $n$ of the hidden rooks by making no more than $\lceil \log _ { 2 } n \rceil + 2$ queries.

In each query, you need to split the chessboard into two tiers by determining which squares should comprise the upper tier and which the lower tier. The rooks are only allowed to move within the tier to which they belong, and they can leap over gaps but cannot leap over other pieces in the same tier. The definitions of control areas and controlled squares do not change. As a response, the interactor will inform you whether each square is controlled after the splitting. Then, the chessboard will return to the initial state.

![](images/dda6008c22fd6d64a054b9add84df688b040427045e1c3c3b07a591bb6537128.jpg)  
Figure 2: Split the T-shaped upper tier out of the chessboard

You may assume that the locations of all the rooks are fixed in advance. Note again that it is not necessary to find all the rooks when the actual number of rooks is greater than $n$ .

# Interaction Protocol

The first line contains an integer $T$ ( $1 \le T \le 1 0 ^ { 4 }$ ), denoting the number of test cases.

The first line of each test case contains an integer $n$ ( $3 \leq n \leq 5 0 0$ ), denoting the size of the chessboard.   
It is guaranteed that the number of test cases that $n \geq 1 0$ does not exceed 5.

To initiate an interaction behavior, you should firstly print a character opt $( o p t \in \{ { \stackrel { \cdot \cdot } { . } } , { \stackrel { \cdot } { . } } \} )$ in one line and then print an $n \times n$ binary matrix (in $n$ lines, each containing a binary string). After printing the binary matrix, do not forget to flush the output. To do this, use fflush(stdout) or cout.flush() in $\mathrm { C } + +$ , System.out.flush() in Java, or stdout.flush() in Python.

$o p t = { } ^ { \cdot } ?$ ’ represents making a query. If the character in the $x$ -th row and $y$ -th column of your binary matrix is $\cdot _ { 1 } \cdot \mathbf { \tau }$ , the square $( x , y )$ will comprise the upper tier, or otherwise, it will comprise the lower tier. The interactor will return an integer code $\mathit { c o d e } \in \{ 0 , 1 \}$ ) in one line. If $c o d e = 1$ , you will get Wrong Answer because you made more than $\lceil \log _ { 2 } n \rceil + 2$ queries. If $c o d e = 0$ , you will then receive another $n \times n$ binary matrix from the interactor. If the character in the $x$ -th row and $y$ -th column of the received binary matrix is $\cdot _ { 1 } \cdot$ , the square $( x , y )$ is controlled after the splitting, or otherwise, it is uncontrolled.

$o p t = { } ^ { \cdot } !$ ’ represents answering the locations. If the character in the $x$ -th row and $y$ -th column of your binary matrix is $\mathit { \Omega } _ { 1 } \mathit { \Omega } _ { 1 } \mathit { \Omega } _ { 1 }$ , the square $( x , y )$ is occupied by a rook in your judgment, or otherwise, whether it is empty or occupied by a rook will not affect the verification of your answer. The interactor will return an integer code ( $c o d e \in \{ 0 , 1 \}$ ) in one line. If $c o d e = 1$ , you will get Wrong Answer because your answer failed to match the actual locations of the rooks or you did not find enough rooks. If $c o d e = 0$ , you will enter the next test case if the current test case is not the last one.

If you attempt to print opt that is neither ‘ ?’ nor ‘ !’, or if your binary matrix is badly formatted, such as some of the $n$ rows has a length other than $n$ or contains a character that is neither $ { \mathbf { \cdot } } _ { 0 } ,$ nor $\cdot _ { 1 } \cdot$ , the interactor will return $c o d e = - 1$ and you will get Wrong Answer as well.

Please terminate your program immediately when you receive some code other than 0 to avoid any unexpected verdicts.

# Example

<table><tr><td>standard input</td><td>standard output</td></tr><tr><td>1 3</td><td></td></tr><tr><td></td><td>？ 101</td></tr><tr><td></td><td>000</td></tr><tr><td></td><td>101</td></tr><tr><td>0</td><td></td></tr><tr><td>110</td><td></td></tr><tr><td>111</td><td></td></tr><tr><td>111</td><td>？</td></tr><tr><td></td><td>111</td></tr><tr><td></td><td></td></tr><tr><td></td><td>100</td></tr><tr><td>0</td><td>100</td></tr><tr><td>111</td><td></td></tr><tr><td></td><td></td></tr><tr><td>111</td><td></td></tr><tr><td>101</td><td></td></tr><tr><td></td><td></td></tr><tr><td></td><td>！</td></tr><tr><td></td><td>010</td></tr><tr><td></td><td>001</td></tr><tr><td></td><td></td></tr><tr><td></td><td></td></tr><tr><td></td><td>100</td></tr><tr><td>0</td><td></td></tr></table>

# Note

The blank lines in the sample case are added for readability. In your output, extra spaces or blank lines will be ignored.

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

using namespace std;


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


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


static void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}

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

    
    int T = inf.readInt();
    cout << T << endl;

    for (int t = 0; t < T; ++t) {
        int n = inf.readInt();
        vector<string> hidden_board(n);
        for (int i = 0; i < n; ++i) {
            hidden_board[i] = inf.readToken();
        }

        
        cout << n << endl;

        
        
        int current_limit = (int)ceil(log2((double)n)) + 2;
        query_limit += current_limit;
        
        int current_queries = 0;
        bool case_solved = false;

        while (true) {
            string token;
            
            if (!(cin >> token)) {
                finish(_wa, "Unexpected EOF from solution (expected opt)");
            }

            if (token == "?") {
                
                vector<string> query_mask(n);
                for(int i = 0; i < n; ++i) {
                    if (!(cin >> query_mask[i])) finish(_wa, "EOF reading query matrix");
                    if ((int)query_mask[i].size() != n) finish(_wa, "Invalid query matrix dimension");
                    for(char c : query_mask[i]) {
                        if (c != '0' && c != '1') finish(_wa, "Invalid char in query matrix");
                    }
                }

                current_queries++;
                queries++;
                log_metrics(); 

                
                if (current_queries > current_limit) {
                    cout << 1 << endl; 
                    finish(_wa, "Query limit exceeded");
                }

                
                
                
                
                
                
                
                vector<int> row_tier0(n, 0), row_tier1(n, 0);
                vector<int> col_tier0(n, 0), col_tier1(n, 0);

                for(int r = 0; r < n; ++r) {
                    for(int c = 0; c < n; ++c) {
                        if (hidden_board[r][c] == '1') {
                            char tier = query_mask[r][c];
                            if (tier == '0') {
                                row_tier0[r] = 1;
                                col_tier0[c] = 1;
                            } else {
                                row_tier1[r] = 1;
                                col_tier1[c] = 1;
                            }
                        }
                    }
                }

                vector<string> response(n, string(n, '0'));
                for(int r = 0; r < n; ++r) {
                    for(int c = 0; c < n; ++c) {
                        bool controlled = false;
                        if (hidden_board[r][c] == '1') {
                            controlled = true;
                        } else {
                            char tier = query_mask[r][c];
                            if (tier == '0') {
                                if (row_tier0[r] || col_tier0[c]) controlled = true;
                            } else {
                                if (row_tier1[r] || col_tier1[c]) controlled = true;
                            }
                        }
                        response[r][c] = controlled ? '1' : '0';
                    }
                }

                
                cout << 0 << endl;
                for(int i = 0; i < n; ++i) cout << response[i] << endl;
                cout.flush();

            } else if (token == "!") {
                
                vector<string> ans_mask(n);
                for(int i = 0; i < n; ++i) {
                    if (!(cin >> ans_mask[i])) finish(_wa, "EOF reading answer matrix");
                    if ((int)ans_mask[i].size() != n) finish(_wa, "Invalid answer matrix dimension");
                    for(char c : ans_mask[i]) {
                        if (c != '0' && c != '1') finish(_wa, "Invalid char in answer matrix");
                    }
                }

                
                
                
                int found_cnt = 0;
                bool false_positive = false;

                for(int r = 0; r < n; ++r) {
                    for(int c = 0; c < n; ++c) {
                        if (ans_mask[r][c] == '1') {
                            if (hidden_board[r][c] == '1') {
                                found_cnt++;
                            } else {
                                false_positive = true;
                            }
                        }
                    }
                }

                if (!false_positive && found_cnt >= n) {
                    cout << 0 << endl; 
                    case_solved = true;
                    break; 
                } else {
                    cout << 1 << endl; 
                    finish(_wa, "Incorrect answer: false positive or fewer than n rooks found");
                }

            } else {
                
                cout << -1 << endl;
                finish(_wa, "Invalid operator token (expected ? or !)");
            }
        }
    }

    finish(_ok, "All cases passed");
    return 0;
}


Adaptive interactor (C++):

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");
    int n = opt<int>("n", 0);

    
    if (n == 0) {
        
        
        int type = rnd.next(1, 100);
        if (type <= 5) n = rnd.next(3, 10);
        else if (type <= 20) n = rnd.next(11, 50);
        else n = rnd.next(51, 500);
    }
    
    
    if (n < 3) n = 3;
    if (n > 500) n = 500;

    
    vector<string> board(n, string(n, '0'));

    















    int strategy;
    if (mode == "adp") {
        
        
        int r = rnd.next(0, 2);
        if (r == 0) strategy = 0;
        else if (r == 1) strategy = 3;
        else strategy = 4;
    } else {
        
        strategy = rnd.next(0, 5);
    }

    if (strategy == 0) {
        
        vector<int> p(n);
        iota(p.begin(), p.end(), 0);
        shuffle(p.begin(), p.end());
        for (int i = 0; i < n; i++) {
            board[i][p[i]] = '1';
        }
    } else if (strategy == 1) {
        
        for (int i = 0; i < n; i++) {
            int count = rnd.next(1, 2); 
            for (int k = 0; k < count; k++) {
                board[i][rnd.next(0, n - 1)] = '1';
            }
        }
    } else if (strategy == 2) {
        
        for (int j = 0; j < n; j++) {
            int count = rnd.next(1, 2);
            for (int k = 0; k < count; k++) {
                board[rnd.next(0, n - 1)][j] = '1';
            }
        }
    } else if (strategy == 3) {
        
        int c = rnd.next(0, n - 1);
        for (int i = 0; i < n; i++) board[i][c] = '1';
        
        int noise = rnd.next(0, n);
        for (int k = 0; k < noise; k++) {
            board[rnd.next(0, n - 1)][rnd.next(0, n - 1)] = '1';
        }
    } else if (strategy == 4) {
        
        int r = rnd.next(0, n - 1);
        for (int j = 0; j < n; j++) board[r][j] = '1';
        
        int noise = rnd.next(0, n);
        for (int k = 0; k < noise; k++) {
            board[rnd.next(0, n - 1)][rnd.next(0, n - 1)] = '1';
        }
    } else {
        
        
        if (rnd.next(0, 1)) {
            
            for (int i = 0; i < n; i++) board[i][rnd.next(0, n - 1)] = '1';
        } else {
            
            for (int j = 0; j < n; j++) board[rnd.next(0, n - 1)][j] = '1';
        }
        
        int count = rnd.next(n, n * n / 2);
        for (int k = 0; k < count; k++) {
            board[rnd.next(0, n - 1)][rnd.next(0, n - 1)] = '1';
        }
    }

    
    cout << 1 << "\n";
    cout << n << "\n";
    for (int i = 0; i < n; i++) {
        cout << board[i] << "\n";
    }

    return 0;
}