Problem: icpc2025_asia_nanjing_a
Difficulty: medium
Categories: data structures, math
Time limit: 1000 ms, memory limit: 1024 MB

# This is an interactive problem.

After the great success from 2018 to 2024, the Nanjing University of Aeronautics and Astronautics (NUAA) will host the International Collegiate Programming Contest (ICPC) Nanjing regional for the eighth time in a row.

Team Power of Two and team Three Hold Two won the champion title for Tsinghua University in 2018 and 2019. In 2020, 2021, and 2022, team Inverted Cross from Peking University won the three-peat champion titles, while in 2023, another team Reborn as a Vegetable Dog from Peking University won the title. In 2024, team Afterlife from Zhejiang University took the first place.

This year, around 335 teams are participating in the contest. At most 33 gold medals, 66 silver medals, and 99 bronze medals will be awarded (note that these numbers are for reference only). We are looking forward to seeing the participants’ outstanding performance! We also want to express our gratitude for the hard work done by all staff and volunteers for this contest. Thank you all for your great contribution to this contest!

![](images/e9df75b1fe8989a62944d235517cbee122940a20e2dce27ae6aed787c7109ffd.jpg)  
Photo taken in the 2023 ICPC Asia Nanjing Regional Contest

In the 2018 contest, problem K, Kangaroo Puzzle, requires the contestants to construct an operation sequence for the game:

The puzzle is a grid with $n$ rows and $m$ columns ( $1 \leq n , m \leq 2 0$ ), and there are some (at least 2) kangaroos standing in the puzzle. The player’s goal is to control them to get together. There are some walls in some cells, and the kangaroos cannot enter the cells with walls. The other cells are empty. The kangaroos can move from an empty cell to an adjacent empty cell in four directions: up, down, left, and right.

There is exactly one kangaroo in every empty cell in the beginning, and the player can control the kangaroos by pressing the buttons U, D, L, R on the keyboard. The kangaroos will move simultaneously according to the button you press.

The contestant needs to construct an operating sequence of at most $5 \times 1 0 ^ { 4 }$ steps consisting of U, D, L, R only to achieve the goal.

In the 2020 contest, problem A, Ah, It’s Yesterday Once More, requires the contestants to construct an input map to hack the following code of the problem described before:

#include <b i t s / s t d c ++.h>   
using namespace s t d ;   
s t r i n g s = "UDLR" ;   
i n t main ( )   
{ s r a n d ( time (NULL ) ) ; f o r ( i n t i = 1 ; i <= 5 0 0 0 0 ; i ++) p u t c h a r ( s [ rand ( ) % 4 ] ) ; return 0 ;   
}

Furthermore, in the 2021 contest (Problem A, Oops, It’s Yesterday Twice More), the 2022 contest (Problem A, Stop, Yesterday Please No More), the 2023 contest (Problem A, Cool, It’s Yesterday Four Times More), and the 2024 contest (Problem A, Hey, Have You Seen My Kangaroo? ), every year we have a problem related to the kangaroos! We would like to introduce all these problems to you, but if we do so every year, we may have a 500-page statement for one single problem in the 3025 contest. Therefore, we omit them this time. Besides, you may already have seen them in the practice contest.

Now, in the 2025 contest, as everyone expects, the kangaroo problem is back again! We don’t know why problem setters are so obsessed with kangaroos, but the problem is as follows:

You are given a grid with $n$ rows and $m$ columns. There is a hole in the cell on the $i _ { h }$ -th row and the $j _ { h }$ -th column. All other cells are empty and there is one kangaroo standing in each cell.

Similarly, the kangaroos are controlled by pressing the button U, D, L, R on the keyboard. All kangaroos will move simultaneously according to the button pressed. Specifically, for any kangaroo located in the cell on the $i$ -th row and the $j$ -th column, indicated by $( i , j )$ :

1. Button U: it will move to $( i - 1 , j )$ .   
2. Button D: it will move to $( i + 1 , j )$ .   
3. Button L: it will move to $( i , j - 1 )$ .   
4. Button R: it will move to $( i , j + 1 )$ .

If a kangaroo steps onto the hole (that is, $i = i _ { h }$ and $j = j _ { h }$ ) or steps out of the grid, it will be removed from the grid.

The problem is that, the exact value of $i _ { h }$ and $j _ { h }$ is not known. Your task is to find the position of the hole with at most $( n + m + 1 0 )$ queries. For each query, you can press one button (U, D, L, or R). The interactor will output an integer $t$ as the answer, indicating the number of kangaroos remaining after you press the button.

Note that the interactor is not adaptive, meaning that the answer for each test case is pre-determined.

# Input

There are multiple test cases. The first line of the input contains an integer $T$ ( $1 \leq T \leq 1 0 0$ ) indicating the number of test cases. For each test case:

The first line contains two integers $n$ and $m$ ( $3 \leq n , m \leq 3 0 .$ ) indicating the number of rows and columns in the grid.

# Interaction Protocol

To ask a query, output one line. First output ? followed by a space, then print one upper-case English letter (U, D, L, or R). After flushing your output, your program should read a single integer $t$ indicating the answer to your query. Recall that you can ask at most $( n + m + 1 0 )$ queries for a test case.

If you want to guess the position of the hole, output one line. First output ! followed by a space, then print two integers $i _ { h }$ and $j _ { h }$ ( $1 \leq i _ { h } \leq n$ , $1 \leq j _ { h } \leq m$ ) separated by a space indicating the position. After flushing your output, your program should continue processing the next test case, or exit immediately if there are no more test cases. Note that your guess does not count as a query.

To flush your output, you can use:

• fflush(stdout) (if you use printf) or cout.flush() (if you use cout) in C and C++.   
• System.out.flush() in Java and Kotlin.   
• sys.stdout.flush() in Python.

# Example

<table><tr><td>standard input</td><td>standard output</td></tr><tr><td>2</td><td></td></tr><tr><td>34</td><td>？L</td></tr><tr><td>7</td><td>？D</td></tr><tr><td>3</td><td></td></tr><tr><td>43</td><td>！23</td></tr><tr><td>8</td><td>？U</td></tr><tr><td>5</td><td>？R ！11</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 = 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());
}

struct TestCase {
    int n, m;
    int hr, hc; 
};

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

    
    
    int T = inf.readInt();
    vector<TestCase> cases;
    cases.reserve(T);
    for (int i = 0; i < T; i++) {
        int n = inf.readInt();
        int m = inf.readInt();
        int r = inf.readInt();
        int c = inf.readInt();
        cases.push_back({n, m, r, c});
        
        
        query_limit += (n + m + 10);
    }

    
    log_metrics();

    
    cout << T << endl;

    
    long long hard_cap = max(200000LL, 10 * query_limit);

    
    for (int t_idx = 0; t_idx < T; t_idx++) {
        const auto& tc = cases[t_idx];
        
        
        cout << tc.n << " " << tc.m << endl;

        
        
        vector<pair<int, int>> roos;
        roos.reserve(tc.n * tc.m);
        for (int r = 1; r <= tc.n; r++) {
            for (int c = 1; c <= tc.m; c++) {
                if (r == tc.hr && c == tc.hc) continue;
                roos.push_back({r, c});
            }
        }

        bool case_solved = false;
        while (!case_solved) {
            string token;
            if (!(cin >> token)) {
                finish(_wa, "Unexpected EOF while waiting for query/guess");
            }

            bool is_query = false;
            bool is_guess = false;
            char move_dir = 0;
            int guess_r = -1, guess_c = -1;

            if (token == "!") {
                
                is_guess = true;
                if (!(cin >> guess_r >> guess_c)) {
                    finish(_wa, "Unexpected EOF reading guess coordinates");
                }
            } else if (token == "?") {
                
                is_query = true;
                string dir_str;
                if (!(cin >> dir_str)) {
                    finish(_wa, "Unexpected EOF reading query direction");
                }
                if (dir_str.size() != 1) {
                    finish(_pe, "Invalid direction format");
                }
                move_dir = dir_str[0];
            } else {
                
                if (token.size() == 1 && string("UDLR").find(token) != string::npos) {
                    is_query = true;
                    move_dir = token[0];
                } else {
                    finish(_pe, "Invalid token received: " + token);
                }
            }

            if (is_guess) {
                if (guess_r == tc.hr && guess_c == tc.hc) {
                    case_solved = true;
                    
                } else {
                    finish(_wa, format("Incorrect guess: (%d, %d), expected (%d, %d)", 
                                     guess_r, guess_c, tc.hr, tc.hc));
                }
            } else if (is_query) {
                queries++;
                
                
                if (queries > hard_cap) {
                    finish(_pe, "Hard query limit exceeded");
                }
                
                if (queries % 100 == 0) log_metrics();

                
                int dr = 0, dc = 0;
                if (move_dir == 'U') dr = -1;
                else if (move_dir == 'D') dr = 1;
                else if (move_dir == 'L') dc = -1;
                else if (move_dir == 'R') dc = 1;
                else finish(_pe, string("Unknown direction: ") + move_dir);

                
                int write_idx = 0;
                for (size_t k = 0; k < roos.size(); k++) {
                    roos[k].first += dr;
                    roos[k].second += dc;

                    bool alive = true;
                    
                    if (roos[k].first < 1 || roos[k].first > tc.n) alive = false;
                    if (roos[k].second < 1 || roos[k].second > tc.m) alive = false;
                    
                    if (alive && roos[k].first == tc.hr && roos[k].second == tc.hc) alive = false;

                    if (alive) {
                        roos[write_idx++] = roos[k];
                    }
                }
                roos.resize(write_idx);

                
                cout << roos.size() << endl;
            }
        }
    }

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


Adaptive interactor (C++):

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

struct TestCase {
    int n, m, r, c;
};

int main(int argc, char* argv[]) {
    registerGen(argc, argv, 1);

    string mode = opt<string>("mode", "non");
    long long seedVal = 0;
    
    if (argc > 1) {
        seedVal = atoll(argv[1]);
    }

    vector<TestCase> cases;

    if (seedVal == 1) {
        
        
        for (int r = 1; r <= 3; r++) {
            for (int c = 1; c <= 3; c++) {
                cases.push_back({3, 3, r, c});
            }
        }
        
        cases.push_back({3, 4, 1, 1});
        cases.push_back({3, 4, 1, 4});
        cases.push_back({3, 4, 3, 1});
        cases.push_back({3, 4, 3, 4});
        
        cases.push_back({4, 3, 1, 1});
        cases.push_back({4, 3, 1, 3});
        cases.push_back({4, 3, 4, 1});
        cases.push_back({4, 3, 4, 3});
    }
    else if (seedVal == 2) {
        
        int n = 30, m = 30;
        
        cases.push_back({n, m, 1, 1});
        cases.push_back({n, m, 1, m});
        cases.push_back({n, m, n, 1});
        cases.push_back({n, m, n, m});
        cases.push_back({n, m, n/2, m/2});
        
        for (int i = 0; i < 15; i++) {
            cases.push_back({n, m, rnd.next(1, n), rnd.next(1, m)});
        }
    }
    else if (seedVal == 3) {
        
        
        for (int i = 0; i < 10; i++) {
            cases.push_back({3, 30, rnd.next(1, 3), rnd.next(1, 30)});
        }
        
        for (int i = 0; i < 10; i++) {
            cases.push_back({30, 3, rnd.next(1, 30), rnd.next(1, 3)});
        }
    }
    else {
        
        int T = 100;
        for (int i = 0; i < T; i++) {
            int n = rnd.next(3, 30);
            int m = rnd.next(3, 30);
            int r = rnd.next(1, n);
            int c = rnd.next(1, m);
            cases.push_back({n, m, r, c});
        }
    }

    
    
    
    cout << cases.size() << "\n";
    for (const auto& tc : cases) {
        if (mode == "non") {
            cout << tc.n << " " << tc.m << " " << tc.r << " " << tc.c << "\n";
        } else {
            cout << tc.n << " " << tc.m << "\n";
        }
    }

    return 0;
}