Problem: icpc2024_asia_yokohama_regional_g
Difficulty: hard
Categories: graph, search
Time limit: 4000 ms, memory limit: 256 MB

You are standing at the very center of a field, which is divided into a grid of cells running north-south and east-west. A great treasure is hidden somewhere within one of these cells.

John Belzoni, a descendant of the famous treasure hunter Giovanni Battista Belzoni, actually discovered that treasure. Unfortunately enough, he died of heatstroke before successfully digging it out; he seems to have spent too long time wandering around the field.

John started his exploration from the central cell of the field where you stand now. All of his footprints leading to the treasure are left on the field, but you cannot identify the footprint on a cell until you reach there. The footprint on a cell indicates which of the four adjacent grid cells he proceeded to. It is known that John did not visit the same grid cell twice or more. You see a footprint on the central cell indicating that John’s first step was northward.

There is exactly one treasure cell in the field, and you will recognize it only when you are on it.

A possible situation of the field is shown in Figure G.1. John’s footprints are depicted as arrows in the cells. The treasure cell is depicted as ${ \bf \epsilon } ^ { \bullet } ( { \bf \vec { \tau } } _ { G } ^ { \bullet }$ . The shaded cell is where you initially stand.

![](images/4b0d167fa585b05654e0a3ebef224e7744cae45171f65111f2408ccac00e544b.jpg)  
Figure G.1. Possible situation

Your task is to find the treasure in a limited number of steps. In a single step, you decide to take one of the four directions north, west, south, or east, and proceed to the adjacent cell in that direction. When you move on to the cell, you may find either the treasure, John’s footprint, or nothing. You do not have to follow John’s footprints. Unlike John’s route, you may visit the same cell more than once. John’s footprints will remain the same regardless of your exploration.

# Interaction

The interaction begins by receiving an integer $n$ $1 \leq n \leq 2 0 0 0 )$ ) from the standard input, followed by a newline. The integer $n$ means that the field is partitioned into $( 2 n + 1 ) \times ( 2 n + 1 )$ grid cells. You are initially on the cell $( n + 1 )$ -th from the west end and $( n + 1 )$ -th from the north end. After receiving the integer $n$ , you can start your exploration steps.

In each step, you send a single character meaning the direction of the cell to move to: ‘ˆ’ (caret) for north, $\cdot _ { < } ,$ (less than symbol) for west, ‘v’ (lowercase V) for south, and $\bullet _ { > } ,$ (greater than symbol) for east. The character should be sent to the standard output followed by a newline.

In its response, you will receive a character indicating what you find on the cell you moved to, followed by a newline. The character ‘G’ means that the treasure is there. The characters ‘ $\because \mathrm { ~  ~ \cdot ~ } < , \mathrm { ~  ~ \cdot ~ } _ { \mathrm { { V } } } ,$ , and $^ \bullet > ^ { : }$ ’ mean John’s footprint directing north, west, south, and east, respectively. The character ‘.’ (dot) means neither the treasure nor a footprint is on the cell.

When you find the treasure, that is, when you have received the character $\mathbf { \epsilon } ^ { \bullet } ( \mathbf { \vec { \jmath } } _ { G } ^ { \ \ \bullet }$ , the interaction stops and your program should terminate. You must reach the treasure cell within 30000 steps. Although following John’s steps will surely lead you to the treasure, that may require more than 30000 steps.

In any of the following cases, your submission will be judged as a wrong answer.

• The output format of your program is invalid.   
• You specify a direction to move out of the grid.   
• Extra outputs are made after reaching the treasure cell.   
• You could not reach the treasure cell within 30000 steps.

The arrangement of the field (the place of the treasure cell and John’s footprints) is fixed before the interaction starts; it does not change during the interaction.

As some environments require flushing the output buffers, make sure that your outputs are actually sent. Otherwise, your outputs will never reach the judge. You are provided with a command-line tool for local testing. For more details, refer to the clarification in the contest system.

![](images/070e0d349e65cf8f76a10a085cf628e77c1f2b42ad7ee4f1f42ad2137bc62696.jpg)

In this interaction, the situation of the field in Figure G.1 is assumed.

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


static long long queries = 0;
static long long query_limit = 30000; 
static const long long HARD_CAP = 100000; 


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 n = inf.readInt();
    string path = inf.readToken();

    
    
    
    
    int side = 2 * n + 1;
    
    
    
    vector<string> grid(side, string(side, '.'));

    int r = n;
    int c = n;

    
    
    for (char move : path) {
        
        grid[r][c] = move;

        
        if (move == '^') r--;
        else if (move == 'v') r++;
        else if (move == '<') c--;
        else if (move == '>') c++;
        else {
            finish(_fail, "Invalid character in hidden input path");
        }

        
        if (r < 0 || r >= side || c < 0 || c >= side) {
            finish(_fail, "Hidden path goes out of bounds");
        }
    }
    
    grid[r][c] = 'G';

    
    
    log_metrics();

    
    cout << n << endl; 

    
    int cur_r = n;
    int cur_c = n;

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

        
        
        string token;
        cin >> token;

        
        if (cin.fail()) {
            finish(_wa, "Unexpected EOF (expected move direction)");
        }

        
        if (token.size() != 1) {
            finish(_wa, "Invalid input format: expected single character");
        }

        char move = token[0];
        int dr = 0, dc = 0;

        
        if (move == '^') dr = -1;
        else if (move == 'v') dr = 1;
        else if (move == '<') dc = -1;
        else if (move == '>') dc = 1;
        else {
            finish(_wa, "Invalid direction character");
        }

        
        cur_r += dr;
        cur_c += dc;
        queries++;

        
        if (queries % 100 == 0) {
            log_metrics();
        }

        
        if (cur_r < 0 || cur_r >= side || cur_c < 0 || cur_c >= side) {
            finish(_wa, "Move out of grid bounds");
        }

        
        char content = grid[cur_r][cur_c];

        
        cout << content << endl;

        
        if (content == 'G') {
            finish(_ok, "Treasure found");
        }
    }

    return 0;
}


Adaptive interactor (C++):

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





int dr[] = {-1, 0, 1, 0};
int dc[] = {0, 1, 0, -1};
char dchar[] = {'^', '>', 'v', '<'};








string generate_spiral(int n, int mirror) {
    vector<int> cycle;
    if (mirror == 0) cycle = {0, 1, 2, 3}; 
    else cycle = {0, 3, 2, 1};             

    int r = n, c = n; 
    string path = "";
    
    
    
    
    
    
    int dir_idx = 0; 
    
    
    int max_len = 200000;
    
    bool done = false;
    for (int len_idx = 0; ; ++len_idx) {
        int current_len = (len_idx / 2) + 1;
        int d = cycle[dir_idx];
        dir_idx = (dir_idx + 1) % 4;
        
        for (int k = 0; k < current_len; ++k) {
            int nr = r + dr[d];
            int nc = c + dc[d];
            
            
            if (nr < 0 || nr > 2 * n || nc < 0 || nc > 2 * n) {
                done = true; 
                break;
            }
            
            path += dchar[d];
            r = nr;
            c = nc;
            
            if ((int)path.size() >= max_len) {
                done = true; 
                break;
            }
        }
        if (done) break;
    }
    return path;
}





string generate_random_snake(int n) {
    set<pair<int,int>> visited;
    int r = n, c = n;
    visited.insert({r, c});
    string path = "";
    
    
    r = r - 1;
    path += '^';
    visited.insert({r, c});
    
    int max_len = 50000; 
    
    while ((int)path.size() < max_len) {
        vector<int> cands;
        for(int i=0; i<4; ++i) {
            int nr = r + dr[i];
            int nc = c + dc[i];
            
            if (nr >= 0 && nr <= 2*n && nc >= 0 && nc <= 2*n) {
                
                if (visited.find({nr, nc}) == visited.end()) {
                    cands.push_back(i);
                }
            }
        }
        
        if (cands.empty()) break;
        
        int d = cands[rnd.next((int)cands.size())];
        r += dr[d];
        c += dc[d];
        visited.insert({r, c});
        path += dchar[d];
    }
    return path;
}

int main(int argc, char* argv[]) {
    registerGen(argc, argv, 1);
    
    string mode = opt<string>("mode", "non");
    
    if (mode == "adp") {
        
        
        
        int n;
        
        if (rnd.next(10) == 0) n = rnd.next(1, 50);
        else n = rnd.next(50, 2000);
        
        cout << n << endl;
        cout << rnd.next(2000000000) << endl; 
    } else {
        
        
        
        
        int n;
        int type = rnd.next(100);
        if (type < 5) n = rnd.next(1, 10);        
        else if (type < 20) n = rnd.next(11, 100);
        else if (type < 50) n = rnd.next(101, 500);
        else n = rnd.next(501, 2000);             
        
        string path;
        
        
        
        if (rnd.next(3) == 0) {
            path = generate_random_snake(n);
        } else {
            path = generate_spiral(n, rnd.next(2));
        }
        
        
        if (path.empty()) path = "^";
        
        cout << n << endl;
        cout << path << endl;
    }
    
    return 0;
}