Problem: icpc2023_asia_yokohama_regional_k
Difficulty: hard
Categories: search, math
Time limit: 2000 ms, memory limit: 256 MB

This is an interactive problem.

A thin black disk is laid flat on the square bottom of a white box. The sides of the box bottom are $1 0 ^ { 5 }$ units long.

Somehow, you are not allowed to look into the box, but you want to know how large the disk is and where in the box bottom the disk is laid. You know that the shape of the disk is a true circle with an integer units of radius, not less than 100 units, and its center is integer units distant from the sides of the box bottom. The radius of the disk is, of course, not greater than the distances of the center of the disk from any of the sides of the box bottom.

You can probe the disk by projecting a thin line segment of light to the box bottom. As the reflection coecients of the disk and the box bottom are quite di↵erent, from the overall reflection intensity, you can tell the length of the part of the segment that lit the disk.

Your task is to decide the exact position and size of the disk through repetitive probes.

# Interaction

You can repeat probes, each of which is a pair of sending a query and receiving the response to it. You can probe at most 1024 times.

A query should be sent to the standard output in the following format, followed by a newline.

query $x _ { 1 } \ y _ { 1 } \ x _ { 2 } \ y _ { 2 }$

Here, $( x _ { 1 } , y _ { 1 } )$ and $( x _ { 2 } , y _ { 2 } )$ are the positions of the two ends of the line segment of the light. They have to indicate distinct points. The coordinate system is such that one of the corners of the box bottom is the origin $( 0 , 0 )$ and the diagonal corner has the coordinates $( 1 0 ^ { 5 } , 1 0 ^ { 5 } )$ . All of $x _ { 1 }$ $y _ { 1 } , x _ { 2 }$ , and $y _ { 2 }$ should be integers between 0 and $1 0 ^ { 5 }$ , inclusive.

In response to this query, a real number is sent back to the standard input, followed by a newline. The number indicates the length of the part of the segment that lit the disk. It is in decimal notation without exponent part, with 7 digits after the decimal point. The number may contain an absolute error up to $1 0 ^ { - 6 }$ .

When you become sure about the position and the size of the disk through the probes, you can send your answer. The answer should have the center position and the radius of the disk. It should be sent to the standard output in the following format, followed by a newline.

answer x y $r$

Here, $( x , y )$ should be the position of the center of the disk, and $r$ the radius of the disk. All of $x$ , $y$ , and $r$ should be integers.

After sending the answer, your program should terminate without any extra output. Thus, you can send the answer only once.

# Notes on interactive judging

When your output violates any of the conditions above (incorrect answer, invalid format, $x _ { 1 }$ , $y _ { 1 }$ , $x _ { 2 }$ , or $y _ { 2 }$ being out of the range, too many queries, any extra output after sending your answer, and so on), your submission will be judged as a wrong answer. As some environments require flushing the output bu↵ers, 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.

# Sample Interaction

![](images/3625dd17dd7f25689dce6182d4cb53a3b711e5676a5a630493e18ccb74aef060.jpg)

![](images/82b051f686b8f6ba8021728785ee1a8b849030cfa832ca9aa75bcb5340411562.jpg)  
Figure K.1. Sample Interaction

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


static long long queries = 0;

static const long long query_limit = 1024;

static const long long hard_cap = 20000;


struct Circle {
    long long x, y, r;
} hidden;



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


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



double solve_query(long long x1, long long y1, long long x2, long long y2) {
    
    long double x1d = x1, y1d = y1;
    long double x2d = x2, y2d = y2;
    long double cxd = hidden.x, cyd = hidden.y;
    long double rd = hidden.r;

    long double dx = x2d - x1d;
    long double dy = y2d - y1d;
    long double fx = x1d - cxd;
    long double fy = y1d - cyd;

    
    
    
    long double A = dx * dx + dy * dy;
    long double B = 2.0 * (fx * dx + fy * dy);
    long double C = fx * fx + fy * fy - rd * rd;

    
    if (A <= 1e-18) return 0.0;

    long double delta = B * B - 4.0 * A * C;

    
    if (delta < 0) return 0.0;

    long double sqrt_delta = sqrt(delta);
    long double t1 = (-B - sqrt_delta) / (2.0 * A);
    long double t2 = (-B + sqrt_delta) / (2.0 * A);

    
    
    long double start = max((long double)0.0, t1);
    long double end = min((long double)1.0, t2);

    if (start < end) {
        
        return (double)((end - start) * sqrt(A));
    }
    return 0.0;
}

bool in_range(long long val) {
    return val >= 0 && val <= 100000;
}

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

    
    
    hidden.x = inf.readInt();
    hidden.y = inf.readInt();
    hidden.r = inf.readInt();

    
    log_metrics();

    
    
    cout << fixed << setprecision(7);

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

        string token;
        if (!(cin >> token)) {
            finish(_wa, "Unexpected EOF (expected query or answer)");
        }

        
        if (token == "answer" || token == "!") {
            long long ax, ay, ar;
            if (!(cin >> ax >> ay >> ar)) {
                finish(_wa, "Invalid answer format");
            }
            
            
            if (ax == hidden.x && ay == hidden.y && ar == hidden.r) {
                finish(_ok, "Correct answer");
            } else {
                string msg = "Wrong answer. Expected: " + to_string(hidden.x) + " " 
                           + to_string(hidden.y) + " " + to_string(hidden.r) 
                           + ", Found: " + to_string(ax) + " " + to_string(ay) + " " + to_string(ar);
                finish(_wa, msg);
            }
        } 
        else {
            
            long long x1, y1, x2, y2;
            bool explicit_query = (token == "query" || token == "?");

            if (explicit_query) {
                if (!(cin >> x1 >> y1 >> x2 >> y2)) {
                    finish(_wa, "Invalid query format");
                }
            } else {
                
                try {
                    size_t pos;
                    x1 = stoll(token, &pos);
                    if (pos != token.size()) finish(_wa, "Invalid token in query");
                } catch (...) {
                    finish(_wa, "Invalid token in query");
                }
                if (!(cin >> y1 >> x2 >> y2)) {
                    finish(_wa, "Invalid query format");
                }
            }

            
            if (!in_range(x1) || !in_range(y1) || !in_range(x2) || !in_range(y2)) {
                finish(_wa, "Query coordinates out of bounds [0, 100000]");
            }
            if (x1 == x2 && y1 == y2) {
                finish(_wa, "Query points must be distinct");
            }

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

            double result = solve_query(x1, y1, x2, y2);
            cout << result << endl; 
        }
    }

    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 = 1024;
static bool metrics_logged = false;


void log_metrics() {
    if (metrics_logged) return;
    metrics_logged = true;
    try {
        if (tout.is_open()) {
            tout << "queries=" << queries << endl;
            tout << "query_limit=" << query_limit << endl;
        }
    } catch (...) {}
}


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


typedef long long ll;
typedef long double ld;
const ld EPS = 1e-9;
const int BOX_SIZE = 100000;

struct Circle {
    ll x, y, r;
};


ld get_intersection(const Circle& C, ll x1, ll y1, ll x2, ll y2) {
    ld cx = C.x, cy = C.y, cr = C.r;
    ld dx = x2 - x1;
    ld dy = y2 - y1;
    ld len_sq = dx*dx + dy*dy;
    
    if (len_sq < EPS) return 0.0;
    
    ld len = sqrt(len_sq);
    ld ux = dx / len;
    ld uy = dy / len;
    
    
    
    
    ld t_m = (cx - x1) * ux + (cy - y1) * uy;
    
    
    ld mx = x1 + t_m * ux;
    ld my = y1 + t_m * uy;
    ld dist_sq = (cx - mx)*(cx - mx) + (cy - my)*(cy - my);
    
    
    if (dist_sq > cr*cr) return 0.0;
    
    ld chord_half = sqrt(max((ld)0.0, cr*cr - dist_sq));
    
    
    ld t_start = t_m - chord_half;
    ld t_end = t_m + chord_half;
    
    
    ld inter_start = max(t_start, (ld)0.0);
    ld inter_end = min(t_end, len);
    
    if (inter_start < inter_end) {
        return inter_end - inter_start;
    }
    return 0.0;
}


vector<Circle> candidates;
const int POOL_SIZE = 5000;

void init_pool(int tx, int ty, int tr) {
    candidates.reserve(POOL_SIZE);
    
    
    candidates.push_back({(ll)tx, (ll)ty, (ll)tr});
    
    
    mt19937 rng(1337 + tx + ty * 100 + tr);
    
    auto add_candidate = [&](ll x, ll y, ll r) {
        if (r < 100) return;
        if (x < r || x > BOX_SIZE - r) return;
        if (y < r || y > BOX_SIZE - r) return;
        candidates.push_back({x, y, r});
    };
    
    
    for (int i = 0; i < POOL_SIZE * 3 && candidates.size() < POOL_SIZE; ++i) {
        int mode = uniform_int_distribution<int>(0, 100)(rng);
        ll r, x, y;
        
        
        if (mode < 20) r = uniform_int_distribution<int>(100, 1000)(rng);
        else if (mode < 40) r = uniform_int_distribution<int>(20000, 50000)(rng);
        else r = uniform_int_distribution<int>(100, 50000)(rng);
        
        
        if (mode > 80) { 
            
             int side = uniform_int_distribution<int>(0, 3)(rng);
             if (side == 0) x = r + uniform_int_distribution<int>(0, 500)(rng);
             else if (side == 1) x = BOX_SIZE - r - uniform_int_distribution<int>(0, 500)(rng);
             else x = uniform_int_distribution<int>(r, BOX_SIZE - r)(rng);
             
             if (side == 2) y = r + uniform_int_distribution<int>(0, 500)(rng);
             else if (side == 3) y = BOX_SIZE - r - uniform_int_distribution<int>(0, 500)(rng);
             else y = uniform_int_distribution<int>(r, BOX_SIZE - r)(rng);
        } else {
             x = uniform_int_distribution<int>(r, BOX_SIZE - r)(rng);
             y = uniform_int_distribution<int>(r, BOX_SIZE - r)(rng);
        }
        
        add_candidate(x, y, r);
    }
}

int main(int argc, char** argv) {
    registerInteraction(argc, argv);
    atexit(log_metrics);
    
    
    int tx = inf.readInt();
    int ty = inf.readInt();
    int tr = inf.readInt();
    
    
    init_pool(tx, ty, tr);
    
    cout << fixed << setprecision(7);
    
    while (true) {
        string token;
        if (!(cin >> token)) {
            
            finish(_wa, "Unexpected EOF (expected query or answer)");
        }
        
        if (token == "query") {
            queries++;
            
            if (queries > 2000) {
                finish(_pe, "Hard query limit exceeded (>2000)");
            }
            
            ll x1, y1, x2, y2;
            if (!(cin >> x1 >> y1 >> x2 >> y2)) {
                finish(_wa, "Invalid query format");
            }
            
            
            auto valid = [&](ll v) { return v >= 0 && v <= BOX_SIZE; };
            if (!valid(x1) || !valid(y1) || !valid(x2) || !valid(y2)) {
                finish(_wa, "Query coordinates out of bounds");
            }
            if (x1 == x2 && y1 == y2) {
                finish(_wa, "Query points must be distinct");
            }
            
            
            if (queries % 50 == 0) log_metrics();

            
            vector<pair<ld, int>> outcomes;
            outcomes.reserve(candidates.size());
            for (int i = 0; i < (int)candidates.size(); ++i) {
                outcomes.push_back({get_intersection(candidates[i], x1, y1, x2, y2), i});
            }
            
            
            sort(outcomes.begin(), outcomes.end(), [](const auto& a, const auto& b){
                return a.first < b.first;
            });
            
            
            
            
            
            
            ld WINDOW = 2.0e-6;
            int max_cnt = 0;
            int best_start = 0;
            
            int r = 0;
            for (int l = 0; l < (int)outcomes.size(); ++l) {
                while (r < (int)outcomes.size() && outcomes[r].first - outcomes[l].first <= WINDOW + 1e-15) {
                    r++;
                }
                int cnt = r - l;
                if (cnt > max_cnt) {
                    max_cnt = cnt;
                    best_start = l;
                }
            }
            
            
            int best_end = best_start + max_cnt;
            ld min_v = outcomes[best_start].first;
            ld max_v = outcomes[best_end - 1].first;
            ld reply = (min_v + max_v) / 2.0;
            
            vector<Circle> next_pool;
            next_pool.reserve(max_cnt);
            for (int i = best_start; i < best_end; ++i) {
                next_pool.push_back(candidates[outcomes[i].second]);
            }
            candidates = next_pool;
            
            if (candidates.empty()) finish(_wa, "Judge internal error: empty pool");
            
            cout << reply << endl;
            
        } else if (token == "answer") {
            ll ax, ay, ar;
            if (!(cin >> ax >> ay >> ar)) {
                finish(_wa, "Invalid answer format");
            }
            
            
            bool mismatch = false;
            for (const auto& c : candidates) {
                if (c.x != ax || c.y != ay || c.r != ar) {
                    mismatch = true;
                    break;
                }
            }
            
            if (mismatch) {
                
                finish(_wa, "Answer is ambiguous or incorrect (multiple valid configurations remain)");
            } else {
                finish(_ok, "Correct");
            }
            
        } else {
            finish(_wa, "Unknown token: " + token);
        }
    }
    
    return 0;
}


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

const int BOX_SIZE = 100000;

int main(int argc, char* argv[]) {
    
    
    registerGen(argc, argv, 1);
    
    
    string mode = opt<string>("mode", "non");

    
    long long seed_val = 0;
    if (argc > 1) {
        try {
            seed_val = stoll(string(argv[1]));
        } catch (...) {
            seed_val = 0; 
        }
    }

    int x, y, r;

    
    
    
    
    if (seed_val == 1) {
        
        r = 100;
        x = 100;
        y = 100;
    } else if (seed_val == 2) {
        
        r = 100;
        x = BOX_SIZE - 100;
        y = BOX_SIZE - 100;
    } else if (seed_val == 3) {
        
        r = 50000;
        x = 50000;
        y = 50000;
    } else if (seed_val == 4) {
        
        r = 100;
        x = 50000;
        y = 50000;
    } else if (seed_val == 5) {
        
        r = 100;
        x = rnd.next(r, BOX_SIZE - r);
        y = rnd.next(r, BOX_SIZE - r);
    } else {
        
        
        
        int type = rnd.next(100);
        if (type < 50) {
            
            r = rnd.next(100, 1000);
        } else if (type < 80) {
            
            r = rnd.next(1001, 20000);
        } else {
            
            r = rnd.next(20001, 50000);
        }
        
        
        
        x = rnd.next(r, BOX_SIZE - r);
        y = rnd.next(r, BOX_SIZE - r);
    }

    
    
    if (mode == "non") {
        cout << x << " " << y << " " << r << endl;
    } else if (mode == "adp") {
        
        
        cout << x << " " << y << " " << r << endl;
    }

    return 0;
}