Problem: icpc2025_asia_chengdu_e
Difficulty: easy
Categories: math
Time limit: 3000 ms, memory limit: 1024 MB

# This is an interactive problem.

Panda, an adventurous child, has accidentally activated a trap while exploring ruins. The trap is a regular polygon with $N$ vertices, numbered $1 , 2 , \ldots , N$ in a counterclockwise direction. The trap’s center (i.e., the centroid of the polygon) must be destroyed if he wants to escape from the trap. (A regular polygon is a convex polygon that is both equilateral, meaning all sides have the same length, and equiangular, meaning all interior angles are equal.)

Panda doesn’t know the side length of the polygon or his exact location. He might be inside, on the boundary, or outside the regular polygon. His only tool is a measurement method: he can choose any two polygon vertices, $a$ and $b$ , and his tool will tell him the area of the triangle formed by his current position and these two vertices. Time is short, and he has at most 5 measurement attempts. Your task is to help him calculate the distance from his current position to the polygon’s centroid so that he can summon a ring of fire to destroy the trap.

# Interaction Protocol

This problem contains multiple sets of interactive tests. First, your program should read an integer $T$ from standard input ( $1 \leq T \leq 1 0 ^ { 4 }$ ), indicating that there are $T$ sets of interactive tests.

At the beginning of each test set, your program should read an integer $N$ from standard input ( $4 \leq N \leq 2 0 0$ ), representing the number of vertices of the regular polygon. It is guaranteed that the circumradius of this regular polygon (the distance from the polygon’s centroid to any vertex) is a real number in the range [1, 100], and the distance from Panda’s position to the centroid of the polygon (i.e., the answer) will not exceed 10000. All hidden parameters remain fixed during each test case, including the circumradius of the polygon, Panda’s position, and the exact coordinates of the polygon’s vertices.

For a measurement query, output a line to standard output in the form of $\ ? \texttt { x y }$ , where $x , y$ are two integers from 1 to $N$ , indicating the indices of the points on the regular polygon you want to query. The interaction system will return a non-negative real number with at least 10 valid digits, representing the area of the triangle formed by these two vertices and Panda’s position. Please note that if your program makes more than 5 queries, the interactor will output a line -1 and stop the interaction, and your solution will receive Wrong Answer.

After at most 5 queries, output a line to standard output in the form of $\mathrm { ~ \pm ~ } \mathsf { d }$ to give the answer, where $d$ is a non-negative real number representing the distance from Panda to the centroid of the regular polygon. Your output will be considered correct if the absolute or relative error between your output $p$ and the interaction system’s computed standard answer $q$ does not exceed $1 0 ^ { - 4 }$ , i.e., $\operatorname* { m i n } \left( \left| { \frac { p - q } { q } } \right| , | p - q | \right) \leq 1 0 ^ { - 4 }$ . In this case, the interactor will output a line Correct and continue to the next set of data for interaction; otherwise, the interactor will output a line Wrong and stop the interaction, and your solution will receive Wrong Answer.

Note: Each output must be followed by a newline, and the standard output buffer must be flushed. To flush the buffer, you can:

• For C or $\mathrm { C } + +$ , use fflush(stdout) or cout.flush().   
• For Java, use System.out.flush().   
• For Python, use stdout.flush().

# Example

<table><tr><td>standard input</td><td>standard output</td></tr><tr><td>1</td><td></td></tr><tr><td>4</td><td></td></tr><tr><td>1.000000000</td><td>？12</td></tr><tr><td>3.000000000</td><td>？23</td></tr><tr><td>3.000000000</td><td>？34</td></tr><tr><td>1.000000000</td><td>？14</td></tr><tr><td>Correct</td><td>！1.000000000</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 const int MAX_QUERIES_PER_CASE = 5;







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


double get_area(Point p, Point a, Point b) {
    
    return 0.5 * std::abs((a.x - p.x) * (b.y - p.y) - (a.y - p.y) * (b.x - p.x));
}




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

    
    
    int T = inf.readInt();
    
    
    query_limit = (long long)T * MAX_QUERIES_PER_CASE;
    log_metrics(); 

    
    cout << T << endl;

    
    for (int t = 0; t < T; ++t) {
        
        
        
        
        
        int N = inf.readInt();
        double R = inf.readDouble();
        double theta = inf.readDouble();
        double Px = inf.readDouble();
        double Py = inf.readDouble();

        
        cout << N << endl;

        
        
        
        vector<Point> poly(N + 1);
        double PI = std::acos(-1.0);
        for (int i = 1; i <= N; ++i) {
            double ang = theta + 2.0 * PI * (i - 1) / N;
            poly[i] = {R * std::cos(ang), R * std::sin(ang)};
        }

        Point P = {Px, Py};
        double true_dist = std::hypot(Px, Py);

        int local_queries = 0;
        bool case_active = true;

        while (case_active) {
            string token;
            
            if (!(cin >> token)) {
                finish(_wa, "Unexpected EOF (contestant exited early)");
            }

            bool is_answer = false;
            
            if (token == "!" || token == "！") {
                is_answer = true;
            } else if (token == "?" || token == "？") {
                is_answer = false; 
            } else {
                
                
                
                
                
                
                is_answer = false;
            }

            if (is_answer) {
                
                double ans;
                if (!(cin >> ans)) {
                    finish(_wa, "Incomplete answer (expected number after !)");
                }

                
                double err = std::abs(ans - true_dist);
                double rel = (true_dist > 1e-9) ? (err / true_dist) : err;

                if (err <= 1e-4 || rel <= 1e-4) {
                    cout << "Correct" << endl;
                    case_active = false; 
                } else {
                    cout << "Wrong" << endl;
                    finish(_wa, format("Wrong Answer. Expected %.6f, got %.6f", true_dist, ans));
                }

            } else {
                
                
                
                queries++;
                local_queries++;
                if (queries % 10 == 0) log_metrics(); 

                
                
                if (local_queries > MAX_QUERIES_PER_CASE) {
                    cout << -1 << endl;
                    finish(_wa, "Too many queries (>5)");
                }

                
                int u = -1, v = -1;
                if (token == "?" || token == "？") {
                    
                    if (!(cin >> u >> v)) finish(_wa, "Incomplete query indices");
                } else {
                    
                    try {
                        u = std::stoi(token);
                    } catch (...) {
                        finish(_wa, format("Invalid query token: '%s'", token.c_str()));
                    }
                    if (!(cin >> v)) finish(_wa, "Incomplete query (expected 2nd index)");
                }

                
                if (u < 1 || u > N || v < 1 || v > N) {
                    finish(_wa, format("Invalid indices %d %d (N=%d)", u, v, N));
                }

                
                double area = get_area(P, poly[u], poly[v]);
                cout << format("%.10f", area) << endl;
            }
        }
    }

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


Adaptive interactor (C++):

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");
    
    
    int seedVal = atoi(argv[1]);

    
    cout << 1 << endl;

    int N;
    double R, theta, Dist;
    double Px = 0.0, Py = 0.0;
    const double PI = acos(-1.0);

    
    auto randD = [&](double l, double r) {
        return rnd.next(l, r);
    };

    
    if (seedVal == 1) {
        
        N = 4;
        R = 10.0;
        theta = 0.0;
        Dist = 0.0;
    } else if (seedVal == 2) {
        
        N = 200;
        R = 100.0;
        theta = 1.234567;
        Dist = 9999.0;
    } else if (seedVal == 3) {
        
        N = 5;
        R = 50.0;
        theta = 0.5;
        Dist = 50.0; 
    } else {
        
        N = rnd.next(4, 200);
        R = randD(1.0, 100.0);
        theta = randD(0.0, 2 * PI);
        
        
        int type = rnd.next(0, 10);
        if (type == 0) {
            Dist = 0.0; 
        } else if (type == 1) {
            Dist = R; 
        } else if (type <= 4) {
            Dist = randD(0.0, R); 
        } else if (type <= 7) {
            Dist = randD(R, 2 * R); 
        } else {
            Dist = randD(2 * R, 10000.0); 
        }
    }

    
    cout << N << endl;

    if (mode == "non") {
        
        
        
        if (seedVal == 1) {
            Px = 0.0; Py = 0.0;
        } else if (seedVal == 3) {
            
            Px = R * cos(theta);
            Py = R * sin(theta);
        } else {
            
            double pAngle = randD(0.0, 2 * PI);
            Px = Dist * cos(pAngle);
            Py = Dist * sin(pAngle);
        }

        cout << format("%.20f %.20f", R, theta) << endl;
        cout << format("%.20f %.20f", Px, Py) << endl;
        
    } else {
        
        
        cout << format("%.20f %.20f", R, Dist) << endl;
    }

    return 0;
}