Problem: cf1773_h
Difficulty: hard
Categories: search, math
Time limit: 3000 ms, memory limit: 1024 MB

This is an interactive problem.

After emigrating to another country, little Hanna came across the fact that playing "Hot and cold" became more difficult: she does not understand the hint phrases! You are to write a helping program for her.

The playground where the game happens is a rectangle, and the treasure is hidden in some point with integer coordinates between 0 and $10^6$, inclusive. Hanna visits several points with valid coordinates. If the visited point contains the hidden treasure, she receives the phrase "Found!" in the local language. Fortunately, the intonations allow her to recognize this phrase — in this problem it will be denoted by an exclamation mark.

Otherwise, for each visited point except for the first one, Hanna receives a phrase in the local language stating whether she is now "Closer", "Further", or "At the same distance" from the treasure, compared to the previous point. The distances are measured in Euclidean metric. After the first visited point, in case of no treasure there, Hanna receives the phrase "Not found" in the local language.

Your program must help Hanna find the treasure by visiting at most 64 points.

Interaction

Your program should print each visited point in a line with two coordinates between 0 and $10^6$, inclusive, and flush the output after each one.

For each visited point, the testing system writes one of the phrases: "Found!", "Closer", "Further", "At the same distance", or "Not found" in some language, consistent throughout the game. The first phrase ends with an exclamation mark, all other phrases do not contain exclamation marks. All phrases contain only Latin letters, spaces and exclamation marks, have lengths between 2 and 30, inclusive, don't start or end with a space, and are pairwise distinct. The phrases are not necessarily coming from any real human language.

After receiving a phrase with an exclamation mark, your program must halt without printing anything else. Visiting points more than once is allowed, and each such visit counts towards the limit of 64 queries.

Examples

Input

```
Tabilmadi

Daha yakin

Daha yakin

Sama distanco

Dalej

Znaydeno!
```

Output

```
500 200

560 230

566 240

566 238

30 239

566 239
```

Input

```
You are lucky today!
```

Output

```
777777 777777
```

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

using namespace std;


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


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 target_x = inf.readInt();
    long long target_y = inf.readInt();
    inf.readLine(); 

    string p_found = inf.readLine();
    string p_closer = inf.readLine();
    string p_further = inf.readLine();
    string p_same = inf.readLine();
    string p_not_found = inf.readLine();

    
    if (p_found.empty() || p_closer.empty()) {
        finish(_fail, "Invalid hidden input: failed to read phrases");
    }

    
    long long prev_dist_sq = -1;
    long long hard_cap = 2000; 

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

        
        string token;
        cin >> token;

        if (cin.eof() || cin.fail()) {
            finish(_wa, "Unexpected EOF (did not find treasure)");
        }

        long long x = -1, y = -1;
        try {
            
            if (token == "?" || token == "!") {
                
                if (!(cin >> token)) {
                    finish(_wa, "Expected X coordinate after prefix");
                }
            }

            
            size_t idx = 0;
            x = stoll(token, &idx);
            if (idx != token.size()) finish(_wa, "Invalid format for X coordinate");

            
            string token_y;
            if (!(cin >> token_y)) finish(_wa, "Expected Y coordinate");
            y = stoll(token_y, &idx);
            if (idx != token_y.size()) finish(_wa, "Invalid format for Y coordinate");

        } catch (...) {
            finish(_wa, "Failed to parse coordinates");
        }

        
        queries++;
        
        if (queries % 10 == 0) log_metrics();

        
        if (x < 0 || x > 1000000 || y < 0 || y > 1000000) {
            finish(_wa, "Coordinates out of bounds [0, 1000000]");
        }

        
        if (x == target_x && y == target_y) {
            cout << p_found << endl; 
            finish(_ok, "Found treasure!");
        }

        
        long long curr_dist_sq = (x - target_x) * (x - target_x) + (y - target_y) * (y - target_y);

        
        if (queries == 1) {
            cout << p_not_found << endl;
        } else {
            if (curr_dist_sq < prev_dist_sq) {
                cout << p_closer << endl;
            } else if (curr_dist_sq > prev_dist_sq) {
                cout << p_further << endl;
            } else {
                cout << p_same << endl;
            }
        }
        
        
        prev_dist_sq = curr_dist_sq;
    }

    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 = 64;

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 {
    double x, y;
};
typedef vector<Point> Polygon;

struct PointInt {
    long long x, y;
    bool operator<(const PointInt& other) const {
        if (x != other.x) return x < other.x;
        return y < other.y;
    }
    bool operator==(const PointInt& other) const {
        return x == other.x && y == other.y;
    }
};


Polygon poly;
vector<PointInt> candidates;
bool phase2 = false; 
PointInt last_p = {-1, -1};
bool first_query = true;


string s_found, s_closer, s_further, s_same, s_notfound;


double get_area(const Polygon& p) {
    double area = 0.0;
    if (p.size() < 3) return 0.0;
    for (size_t i = 0; i < p.size(); ++i) {
        size_t j = (i + 1) % p.size();
        area += (p[i].x * p[j].y - p[j].x * p[i].y);
    }
    return abs(area) / 2.0;
}


Polygon clip(const Polygon& poly, double a, double b, double c) {
    Polygon new_poly;
    if (poly.empty()) return new_poly;

    for (size_t i = 0; i < poly.size(); ++i) {
        Point p1 = poly[i];
        Point p2 = poly[(i + 1) % poly.size()];
        
        double v1 = a * p1.x + b * p1.y + c;
        double v2 = a * p2.x + b * p2.y + c;

        
        if (v1 <= 1e-9) {
            new_poly.push_back(p1);
        }

        
        
        
        if ((v1 > 1e-9 && v2 < -1e-9) || (v1 < -1e-9 && v2 > 1e-9) ||
            (v1 > 1e-9 && v2 <= 1e-9) || (v1 <= 1e-9 && v2 > 1e-9)) {
            
            double num = -c - a * p1.x - b * p1.y;
            double den = a * (p2.x - p1.x) + b * (p2.y - p1.y);
            if (abs(den) > 1e-12) {
                double t = num / den;
                new_poly.push_back({p1.x + t * (p2.x - p1.x), p1.y + t * (p2.y - p1.y)});
            }
        }
    }
    return new_poly;
}


void switch_to_phase2() {
    if (phase2) return;
    
    
    double min_x = 1e18, max_x = -1e18, min_y = 1e18, max_y = -1e18;
    for (auto& p : poly) {
        min_x = min(min_x, p.x); max_x = max(max_x, p.x);
        min_y = min(min_y, p.y); max_y = max(max_y, p.y);
    }
    
    long long lx = max(0LL, (long long)floor(min_x - 1e-7));
    long long rx = min(1000000LL, (long long)ceil(max_x + 1e-7));
    long long ly = max(0LL, (long long)floor(min_y - 1e-7));
    long long ry = min(1000000LL, (long long)ceil(max_y + 1e-7));

    candidates.clear();
    
    
    
    
    for (long long x = lx; x <= rx; ++x) {
        for (long long y = ly; y <= ry; ++y) {
            bool inside = true;
            for (size_t i = 0; i < poly.size(); ++i) {
                Point p1 = poly[i];
                Point p2 = poly[(i + 1) % poly.size()];
                
                
                double cp = (p2.x - p1.x) * (y - p1.y) - (p2.y - p1.y) * (x - p1.x);
                if (cp < -1e-7) { 
                    inside = false;
                    break;
                }
            }
            if (inside) {
                candidates.push_back({x, y});
            }
        }
    }
    phase2 = true;
}

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

    
    
    inf.readInt(); inf.readInt(); 
    inf.readLine(); 

    s_found = inf.readLine();
    s_closer = inf.readLine();
    s_further = inf.readLine();
    s_same = inf.readLine();
    s_notfound = inf.readLine();

    
    poly = {{0, 0}, {1000000, 0}, {1000000, 1000000}, {0, 1000000}};

    
    
    log_metrics();

    while (true) {
        
        if (queries > 200) {
            finish(_pe, "Too many queries (hard cap)");
        }

        
        if (cin.eof()) break;
        string token;
        cin >> token;
        if (cin.fail()) break;
        
        long long x, y;
        try {
            x = stoll(token);
            cin >> y;
        } catch (...) {
            finish(_pe, "Invalid coordinate format");
        }

        if (x < 0 || x > 1000000 || y < 0 || y > 1000000) {
            finish(_pe, "Coordinates out of range");
        }

        queries++;
        PointInt curr_p = {x, y};
        string reply = "";

        
        if (!phase2) {
            double min_x = 1e18, max_x = -1e18, min_y = 1e18, max_y = -1e18;
            for (auto& p : poly) {
                min_x = min(min_x, p.x); max_x = max(max_x, p.x);
                min_y = min(min_y, p.y); max_y = max(max_y, p.y);
            }
            double box_area = (max_x - min_x) * (max_y - min_y);
            
            if (box_area < 5000.0) {
                switch_to_phase2();
            }
        }

        if (first_query) {
            reply = s_notfound;
            first_query = false;
            
            if (phase2) {
                vector<PointInt> next_c;
                for (auto& p : candidates) {
                    if (p.x == curr_p.x && p.y == curr_p.y) continue;
                    next_c.push_back(p);
                }
                candidates = next_c;
                if (candidates.empty()) finish(_wa, "Inconsistent state (first query exhausted all points)");
            }
        } else {
            
            
            
            
            
            long long A = 2LL * (last_p.x - curr_p.x);
            long long B = 2LL * (last_p.y - curr_p.y);
            long long C_val = (curr_p.x * curr_p.x + curr_p.y * curr_p.y) - (last_p.x * last_p.x + last_p.y * last_p.y);

            if (!phase2) {
                
                
                
                
                Polygon poly_closer = clip(poly, A, B, C_val);
                Polygon poly_further = clip(poly, -A, -B, -C_val);
                
                double area_closer = get_area(poly_closer);
                double area_further = get_area(poly_further);
                
                if (area_closer >= area_further) {
                    poly = poly_closer;
                    reply = s_closer;
                } else {
                    poly = poly_further;
                    reply = s_further;
                }
            } else {
                
                vector<PointInt> set_c, set_f, set_s;
                bool curr_is_candidate = false;

                for (auto& p : candidates) {
                    if (p == curr_p) {
                        curr_is_candidate = true;
                        continue;
                    }
                    long long val = A * p.x + B * p.y + C_val;
                    if (val < 0) set_c.push_back(p);
                    else if (val > 0) set_f.push_back(p);
                    else set_s.push_back(p);
                }

                size_t sc = set_c.size();
                size_t sf = set_f.size();
                size_t ss = set_s.size();

                
                
                if (sc == 0 && sf == 0 && ss == 0) {
                    if (curr_is_candidate) {
                        reply = s_found;
                    } else {
                        finish(_wa, "Inconsistent state: no candidates left");
                    }
                } else {
                    
                    if (sc >= sf && sc >= ss) {
                        candidates = set_c;
                        reply = s_closer;
                    } else if (sf >= sc && sf >= ss) {
                        candidates = set_f;
                        reply = s_further;
                    } else {
                        candidates = set_s;
                        reply = s_same;
                    }
                }
            }
        }

        last_p = curr_p;
        cout << reply << endl; 
        
        
        log_metrics();

        
        if (!reply.empty() && reply.back() == '!') {
            finish(_ok, "Contestant found the treasure");
        }
    }
    
    
    finish(_wa, "Stream ended without Found!");
}


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

using namespace std;





















string gen_phrase(bool is_found) {
    string res = "";
    int type = rnd.next(0, 2); 
    
    
    if (type == 0) {
        
        int len = rnd.next(2, is_found ? 29 : 30);
        for (int i = 0; i < len; ++i) {
            int t = rnd.next(0, 52); 
            if (t < 26) res += (char)('a' + t);
            else if (t < 52) res += (char)('A' + (t - 26));
            else res += ' ';
        }
    } 
    
    else if (type == 1) { 
        int words = rnd.next(1, 3);
        for(int w = 0; w < words; ++w) {
            if(w > 0) res += " ";
            int wlen = rnd.next(1, 8);
            for(int i = 0; i < wlen; ++i) {
                res += (char)(rnd.next(0, 1) ? rnd.next('a', 'z') : rnd.next('A', 'Z'));
            }
        }
    } 
    
    else { 
        static const vector<string> bases = {
            "Yes", "No", "Warm", "Cold", "Maybe", "Far", "Near", 
            "Left", "Right", "Stop", "Go", "Same", "Bingo", "Ops",
            "Closer", "Further", "Found", "Distance", "Try", "Again"
        };
        res = bases[rnd.next(0, (int)bases.size() - 1)];
        
        if (rnd.next(0, 2) == 0) {
            res += " ";
            res += (char)('a' + rnd.next(0, 25));
        }
    }

    
    
    while (!res.empty() && res.front() == ' ') res.erase(res.begin());
    while (!res.empty() && res.back() == ' ') res.pop_back();
    
    
    for (char &c : res) if (c == '!') c = 'i';

    
    int max_len = is_found ? 29 : 30; 
    int min_len = is_found ? 1 : 2;   
    
    if ((int)res.length() > max_len) res.resize(max_len);
    while ((int)res.length() < min_len) {
        res += (char)('a' + rnd.next(0, 25));
    }

    
    if (is_found) {
        res += '!';
    }
    
    return res;
}

int main(int argc, char* argv[]) {
    registerGen(argc, argv, 1);
    
    string mode = opt<string>("mode", "non");
    long long seed_val = 0;
    try {
        if (argc > 1) seed_val = stoll(string(argv[1]));
    } catch (...) {}

    
    int N = 1000000;
    int x, y;

    
    if (mode == "non" && seed_val >= 1 && seed_val <= 5) {
        if (seed_val == 1) { x = 0; y = 0; }
        else if (seed_val == 2) { x = N; y = N; }
        else if (seed_val == 3) { x = 0; y = N; }
        else if (seed_val == 4) { x = N; y = 0; }
        else { x = N / 2; y = N / 2; }
    } else {
        
        x = rnd.next(0, N);
        y = rnd.next(0, N);
    }

    println(x, y);

    
    
    vector<string> phrases;
    set<string> seen;

    
    while (true) {
        string s = gen_phrase(true);
        if (seen.find(s) == seen.end()) {
            phrases.push_back(s);
            seen.insert(s);
            break;
        }
    }
    
    
    for (int i = 0; i < 4; ++i) {
        while (true) {
            string s = gen_phrase(false);
            if (seen.find(s) == seen.end()) {
                phrases.push_back(s);
                seen.insert(s);
                break;
            }
        }
    }

    
    for (const string &s : phrases) {
        println(s);
    }

    return 0;
}