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

![](images/208e74b2f332c95161b3e998ea0b496ef8df95cf05a97a2fb22b68ac19de5661.jpg)

As you are studying ancient greek mythology, you stumble upon the legend of Phaethon, son of Helios. After being recognized by Helios as his son, he asks him to drive his chariot. Despite the warnings of Helios that only he can control the horses, Helios obliges, giving Phaethon the control of the chariot.

Tragically, Phaethon loses control of the chariot, which gets too close to the Earth and burns it.

However, you wonder if maybe the story could have ended differently. If Phaethon reaches the temple of Helios, then the chariot can be stopped by Helios himself. For that, you create a model that fits the story.

Greece can be represented as a 2D infinite grid, with Phaethon and its chariot starting at coordinate $( 0 , 0 )$ , facing the $\mathrm { O x }$ axis in the direction of increasing X. The temple of Helios is placed in an unknown position $( X , Y )$ on this 2D grid. Initially, the velocity V of the chariot is 1.

At each timestep, the following happens in order:

1. Phaethon does one of the following 3 actions: turn the chariot left 90 degrees, continue facing front, or turn the chariot right 90 degrees;   
2. The chariot moves V units in the direction it is facing;   
3. $V$ is increased by 1;   
4. The Oracle tells Phaethon the Manhattan distance between his position and the position of the temple of Helios.

The Manhattan distance between (X1, Y1) and (X2, Y2) is $| X 1 - X 2 | + | Y 1 - Y 2 |$ .

If the velocity ever reaches $2 \times 1 0 ^ { 4 } .$ , the chariot gets uncontrolled and too close to the earth. If at the end of a step, the chariot is in $( X , Y )$ , the chariot is stopped.

# Input and Output

This is an interactive problem. As such, no initial input is provided in this problem, and you get to ask a question first.

At each step, you must print a line to act on the direction of the chariot. This line must follow the description "? c" followed by a newline, where c is either L to rotate counterclockwise, R to rotate clockwise, or F to keep the direction unchanged. If it does not respect this format, you will receive a WRONG-ANSWER verdict.

After sending a line, you must always flush the output. Otherwise, you will get the verdict TIMELIMIT.

The oracle then prints a single integer, that you can read on the standard input, denoting the Manhattan distance between your new position (after moving V units in the specified direction) and the temple. V is increased after updating your position.

If this distance is 0, it means you arrived to the temple of Helios and thus should stop interaction immediately (exiting your program), receiving an ACCEPTED verdict.

Notice that you must be in the temple after your move, just flying above the temple with the chariot does not count.

If your velocity ever reaches $2 \times 1 0 ^ { 4 }$ , you will receive the verdict WRONG-ANSWER.

# Limits

$$
 \begin{array} { l } { { \bullet } } \end{array} | X | \leqslant 1 0 ^ { 6 }
$$

# Sample interaction

In this section, to clarify what the input and output should be, $^ { \prime \prime } < ^ { \prime \prime }$ is printed before what your program can read on standard input and $^ { \prime \prime } > { ^ { \prime \prime } }$ is printed before what your program should output. Do not include these characters in your real input/output.

In this example, the temple of Helios (whose position you must guess) is located at $\mathrm { X } { = } 1$ , $\Upsilon { = } 5$ . The left column is the I/O. The right column is the state of the chariot after the requested move happened.

$> \ ? \ \mathtt { F }$ Chariot moves to $( 1 , 0 )$ , orientation $=$ right, $V = 2$ $< ~ 5$ Distance from (1,0) to (1,5) is 5 $> \ ? \ \nsim$ Chariot moves to (1, 2), orientation $\mathsf { \Pi } _ { 1 } = \mathsf { u p } , V = 3$ $< ~ 3$ Distance from (1,2) to (1,5) is 3 $> \ ? \ \mathtt { F }$ Chariot moves to (1, 5), orientatio $\mathsf { \Omega } _ { 1 } = \mathsf { u p } , V = 4$ $< ~ 0$ Success

An interactive Python script where you can enter commands and see how the judge would respond is also available on the Web version of this problem.

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

using namespace std;


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

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);

    
    
    
    long long tx = inf.readLong();
    long long ty = inf.readLong();

    
    long long cx = 0;
    long long cy = 0;
    long long v = 1;
    
    
    
    int dir = 0;

    
    long long dx[] = {1, 0, -1, 0};
    long long dy[] = {0, 1, 0, -1};

    while (true) {
        
        
        if (v >= 20000) {
            finish(_wa, "Velocity reached 20000 limit before finding the temple.");
        }

        string token;
        
        if (!(cin >> token)) {
            finish(_wa, "Unexpected EOF reading move.");
        }

        char c = ' ';
        
        
        if (token == "?") {
            string next_token;
            if (!(cin >> next_token)) {
                finish(_wa, "Unexpected EOF after '?'.");
            }
            if (next_token.size() != 1) {
                finish(_wa, "Invalid move format: expected single character after '?'.");
            }
            c = next_token[0];
        } else if (token == "!") {
            
            finish(_wa, "Invalid token '!'. Expected move '? c' or 'c'.");
        } else {
            
            if (token.size() != 1) {
                finish(_wa, "Invalid move format: expected single character.");
            }
            c = token[0];
        }

        
        if (c == 'L') {
            
            dir = (dir + 1) % 4;
        } else if (c == 'R') {
            
            dir = (dir + 3) % 4;
        } else if (c == 'F') {
            
        } else {
            finish(_wa, format("Unknown move character '%c'. Expected L, R, or F.", c));
        }

        
        cx += dx[dir] * v;
        cy += dy[dir] * v;
        
        
        v++;
        queries++;

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

        
        long long dist = std::abs(cx - tx) + std::abs(cy - ty);

        
        if (dist == 0) {
            cout << 0 << endl;
            finish(_ok, format("Reached temple at (%lld, %lld).", cx, cy));
        }

        
        cout << dist << endl;

        
        
        if (v >= 20000) {
            finish(_wa, "Velocity reached 20000 limit.");
        }
        
        
        if (queries > 25000) {
            finish(_pe, "Too many queries (hard cap).");
        }
    }

    return 0;
}


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















static long long queries = 0;



static long long query_limit = 19999; 
static const long long HARD_CAP = 40000;


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 Point {
    long long x, y;
    bool operator<(const Point& o) const {
        if (x != o.x) return x < o.x;
        return y < o.y;
    }
    bool operator==(const Point& o) const {
        return x == o.x && y == o.y;
    }
};

static long long manhattan(long long x1, long long y1, long long x2, long long y2) {
    return abs(x1 - x2) + abs(y1 - y2);
}


static bool adaptive_mode = false;
static Point fixed_target = {0, 0};
static vector<Point> candidates; 
static long long minX = -1000000, maxX = 1000000;
static long long minY = -1000000, maxY = 1000000;


static long long cx = 0, cy = 0;
static int cdir = 0; 
static long long velocity = 1;


static int dx[] = {1, 0, -1, 0};
static int dy[] = {0, 1, 0, -1};

int main(int argc, char** argv) {
    
    registerInteraction(argc, argv);
    atexit(log_metrics);
    
    
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    
    string token = inf.readToken();
    if (token == "ADAPTIVE") {
        adaptive_mode = true;
        minX = inf.readLong();
        maxX = inf.readLong();
        minY = inf.readLong();
        maxY = inf.readLong();

        
        
        int num_initial = 10000; 
        candidates.reserve(num_initial + 100);

        
        candidates.push_back({minX, minY});
        candidates.push_back({minX, maxY});
        candidates.push_back({maxX, minY});
        candidates.push_back({maxX, maxY});
        candidates.push_back({0, 0});

        
        for (int i = 0; i < num_initial; ++i) {
            if (i < 1000) {
                
                int side = rnd.next(4);
                long long px, py;
                if (side == 0) { px = minX; py = rnd.next(minY, maxY); }
                else if (side == 1) { px = maxX; py = rnd.next(minY, maxY); }
                else if (side == 2) { py = minY; px = rnd.next(minX, maxX); }
                else { py = maxY; px = rnd.next(minX, maxX); }
                candidates.push_back({px, py});
            } else {
                
                candidates.push_back({rnd.next(minX, maxX), rnd.next(minY, maxY)});
            }
        }
        
        
        sort(candidates.begin(), candidates.end());
        candidates.erase(unique(candidates.begin(), candidates.end()), candidates.end());

    } else {
        adaptive_mode = false;
        long long x = stoll(token);
        long long y = inf.readLong();
        fixed_target = {x, y};
    }

    
    log_metrics();

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

        
        char q_type;
        if (!(cin >> q_type)) {
            
            finish(_wa, "Unexpected EOF while reading query type");
        }
        if (q_type != '?') {
            finish(_wa, "Query must start with '?'");
        }

        string cmd_str;
        if (!(cin >> cmd_str)) {
            finish(_wa, "Unexpected EOF while reading command");
        }
        if (cmd_str.size() != 1) {
            finish(_wa, "Command must be a single character");
        }
        char cmd = cmd_str[0];

        
        if (cmd == 'L') {
            cdir = (cdir + 1) % 4;
        } else if (cmd == 'R') {
            cdir = (cdir + 3) % 4;
        } else if (cmd == 'F') {
            
        } else {
            finish(_wa, "Invalid command (expected L, R, or F)");
        }

        
        cx += dx[cdir] * velocity;
        cy += dy[cdir] * velocity;
        velocity++;
        queries++;

        
        long long response_dist = 0;

        if (!adaptive_mode) {
            response_dist = manhattan(cx, cy, fixed_target.x, fixed_target.y);
        } else {
            
            
            
            
            
            static vector<pair<long long, int>> dist_map; 
            dist_map.clear();
            dist_map.reserve(candidates.size());

            for (int i = 0; i < (int)candidates.size(); ++i) {
                long long d = manhattan(cx, cy, candidates[i].x, candidates[i].y);
                dist_map.push_back({d, i});
            }
            sort(dist_map.begin(), dist_map.end());

            long long best_dist = -1;
            int max_count = -1;
            
            long long cur_dist = -1;
            int cur_count = 0;

            
            for (size_t i = 0; i < dist_map.size(); ++i) {
                if (i == 0 || dist_map[i].first != cur_dist) {
                    if (i > 0) {
                        if (cur_count > max_count) {
                            max_count = cur_count;
                            best_dist = cur_dist;
                        } else if (cur_count == max_count) {
                            if (cur_dist > best_dist) best_dist = cur_dist;
                        }
                    }
                    cur_dist = dist_map[i].first;
                    cur_count = 1;
                } else {
                    cur_count++;
                }
            }
            
            if (cur_count > max_count) {
                max_count = cur_count;
                best_dist = cur_dist;
            } else if (cur_count == max_count) {
                if (cur_dist > best_dist) best_dist = cur_dist;
            }

            
            vector<Point> next_candidates;
            next_candidates.reserve(max_count);
            
            auto it = lower_bound(dist_map.begin(), dist_map.end(), make_pair(best_dist, -1));
            while (it != dist_map.end() && it->first == best_dist) {
                next_candidates.push_back(candidates[it->second]);
                it++;
            }
            candidates = move(next_candidates);
            response_dist = best_dist;
        }

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

        
        cout << response_dist << endl;

        
        if (response_dist == 0) {
            finish(_ok, "Target found");
        }
        
        
        
    }

    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 == "adp") {
        cout << "ADAPTIVE -1000000 1000000 -1000000 1000000" << endl;
        return 0;
    }

    
    
    long long x, y;
    long long limit = 1000000;
    
    
    long long seed_val = 0;
    if (argc > 1) {
        seed_val = atoll(argv[1]);
    }

    if (seed_val == 1) {
        
        x = limit; 
        y = limit;
    } else if (seed_val == 2) {
        
        x = -limit; 
        y = -limit;
    } else if (seed_val == 3) {
        
        x = 0; 
        y = 0;
    } else if (seed_val == 4) {
        
        x = -limit; 
        y = limit;
    } else if (seed_val == 5) {
        
        x = limit; 
        y = -limit;
    } else if (seed_val > 0 && seed_val <= 10) {
        
        x = rnd.next(-100, 100);
        y = rnd.next(-100, 100);
    } else {
        
        
        
        
        if (rnd.next(4) == 0) {
            long long x_edge = rnd.next(limit - 5000, limit) * (rnd.next(2) ? 1 : -1);
            long long y_edge = rnd.next(limit - 5000, limit) * (rnd.next(2) ? 1 : -1);
            
            
            int type = rnd.next(3);
            if (type == 0) { 
                x = x_edge;
                y = rnd.next(-limit, limit);
            } else if (type == 1) { 
                x = rnd.next(-limit, limit);
                y = y_edge;
            } else { 
                x = x_edge;
                y = y_edge;
            }
        } else {
            
            x = rnd.next(-limit, limit);
            y = rnd.next(-limit, limit);
        }
    }

    
    cout << x << " " << y << endl;

    return 0;
}