Problem: icpc2024_asia_shenyang_g
Difficulty: hard
Categories: data structures, math
Time limit: 1000 ms, memory limit: 1024 MB

# This is an interactive problem.

You are given a simple polygon, but before giving you the $n$ vertices of the polygon, Little Q has shuffled them.

Now you can ask Little Q no more than $( n - 2 )$ queries, each of which consists of two integers $p$ and $q$ $( 0 \leq p / q \leq 1 0 0 0 , 1 \leq q \leq 1 0 0 0$ ). He will tell you the total length of the points on the line $x = p / q$ that lie inside or on the boundary of the polygon, which can be represented as $r / s$ with two integers $r$ and $s$ $r \geq 0 , s \geq 1 , \operatorname* { g c d } ( r , s ) = 1 \quad$ ). Here $\operatorname { g c d } ( r , s )$ is the greatest common divisor of $r$ and $s$ .

You need to find the area of the polygon, which can also be represented as $u / v$ with two integers $u$ and $v$ $( u \geq 0 , v \geq 1 , \operatorname* { g c d } ( u , v ) = 1 $ ).

The polygon and the order of the vertices are determined before the start of the interaction and do not depend on your queries. In other words, the interactor is not adaptive.

# Input

The first line of the input contains an integer $T$ ( $1 \leq T \leq 1 0 0 0$ ), indicating the number of test cases. Fo each test case:

The first line contains an integer $n$ ( $3 \leq n \leq 1 0 0 0$ ), indicating the number of vertices of the polygon.

Then $n$ lines follow, each containing two integers $x$ and $y$ ( $0 \leq x , y \leq 1 0 0 0$ ) that give the coordinate $( x , y )$ of the vertices of the polygon in shuffled order.

The polygon is simple, i.e., its vertices are distinct and no two edges of the polygon intersect or touch, except that consecutive edges touch at their common vertex. In addition, no two consecutive edges are collinear.

It is guaranteed that the sum of $n$ over all test cases does not exceed $1 0 0 0$ .

# Interaction Protocol

If you want to ask a query, output one line. First output ? followed by a space, then output two integers $p$ and $q$ $0 \leq p / q \leq 1 0 0 0 , 1 \leq q \leq 1 0 0 0 )$ . After flushing your output, your program should read two integers $r$ and $s$ $r \geq 0 , s \geq 1 , \operatorname* { g c d } ( r , s ) = 1$ ), indicating the answer to your query.

If you want to guess the area of the polygon, output one line. First output ! followed by a space, then output two integers $u$ and $v$ $( u \geq 0 , v \geq 1 , \operatorname* { g c d } ( u , v ) = 1 )$ ), indicating that the area of the polygon is $u / v$ . After flushing your output, your program should continue processing the next test case, or exit immediately if there are no more test cases. Note that your guess does not count as a query.

To flush your output, you can use:

• fflush(stdout) (if you use printf) or cout.flush() (if you use cout) in C and C++.   
• System.out.flush() in Java and Kotlin.   
• sys.stdout.flush() in Python.

# Example

<table><tr><td>standard input</td><td>standard output</td></tr><tr><td>2</td><td></td></tr><tr><td>4</td><td></td></tr><tr><td>00</td><td></td></tr><tr><td>13</td><td></td></tr><tr><td>11</td><td></td></tr><tr><td>30</td><td></td></tr><tr><td>21</td><td>？11</td></tr><tr><td></td><td>！31</td></tr><tr><td>3</td><td></td></tr><tr><td>00 999 1000</td><td></td></tr><tr><td>1000 999</td><td></td></tr><tr><td></td><td>？11</td></tr><tr><td>1999 999000</td><td></td></tr></table>

# Note

For the first sample case, there are three candidate polygons, shown in the figure below. Only the leftmost one meets the answer 2 to the query $x = 1$ , and thus the area is 3. The other two have the answer 3 to the query $x = 1$ .

![](images/f59aed068ec3581b51f1f5b75d96ef862841625bccda50035f9493a97ab1a609.jpg)

For the second sample case, there is only one candidate polygon, and thus the area is 1999/2.

Note that the order of the vertices shown in the sample case may not be consistent with the actua provided.

The blank lines in the sample case are added for readability. In your output, extra spaces or blank lines will be ignored.

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 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 Frac {
    long long n, d;
    Frac(long long _n = 0, long long _d = 1) {
        if (_d < 0) { _n = -_n; _d = -_d; }
        long long g = std::gcd(abs(_n), _d);
        if (_d == 0) {
            d = 1; n = 0;
        } else {
            n = _n / g;
            d = _d / g;
        }
    }
    bool operator<(const Frac& other) const {
        
        
        return n * other.d < other.n * d;
    }
    bool operator==(const Frac& other) const {
        return n == other.n && d == other.d;
    }
    Frac operator+(const Frac& other) const {
        long long L = std::lcm(d, other.d);
        return Frac(n * (L/d) + other.n * (L/other.d), L);
    }
    Frac operator-(const Frac& other) const {
        long long L = std::lcm(d, other.d);
        return Frac(n * (L/d) - other.n * (L/other.d), L);
    }
};

struct Point {
    long long x, y;
};


std::mt19937 rng(1337);

void solve_case() {
    int n = inf.readInt();
    
    if (n > 2) query_limit += (n - 2);
    log_metrics(); 

    vector<Point> poly(n);
    for (int i = 0; i < n; ++i) {
        poly[i].x = inf.readLong();
        poly[i].y = inf.readLong();
    }

    
    vector<Point> shuffled = poly;
    shuffle(shuffled.begin(), shuffled.end(), rng);

    cout << n << endl;
    for (const auto& p : shuffled) {
        cout << p.x << " " << p.y << "\n";
    }
    cout.flush();

    while (true) {
        string token;
        cin >> token;
        if (cin.fail()) finish(_wa, "Unexpected end of stream from contestant");

        if (token == "!") {
            long long u, v;
            cin >> u >> v;
            if (cin.fail()) finish(_wa, "Failed to read answer");

            
            long long area2 = 0;
            for (int i = 0; i < n; ++i) {
                area2 += (poly[i].x * poly[(i + 1) % n].y - poly[(i + 1) % n].x * poly[i].y);
            }
            area2 = abs(area2);
            Frac true_area(area2, 2);
            Frac guessed_area(u, v);

            if (true_area == guessed_area) {
                return; 
            } else {
                finish(_wa, "Wrong area");
            }
        } else {
            long long p, q;
            
            if (token == "?") {
                cin >> p >> q;
            } else {
                try {
                    p = stoll(token);
                } catch(...) {
                    finish(_wa, "Invalid query format");
                }
                cin >> q;
            }
            if (cin.fail()) finish(_wa, "Failed to read query parameters");

            queries++;
            
            if (queries % 10 == 0) log_metrics();
            
            
            if (queries > 200000) finish(_pe, "Hard query limit exceeded");

            
            vector<pair<Frac, Frac>> intervals;

            
            
            
            
            
            vector<pair<Frac, int>> events;
            for (int i = 0; i < n; ++i) {
                Point a = poly[i];
                Point b = poly[(i + 1) % n];
                
                
                if (a.x == b.x) continue;

                bool a_left = (a.x * q < p);
                bool b_left = (b.x * q < p);

                if (a_left != b_left) {
                    
                    
                    
                    long long num = (b.y - a.y) * (p - a.x * q);
                    long long den = (b.x - a.x) * q;
                    Frac term(num, den);
                    Frac y_int = Frac(a.y, 1) + term;
                    events.push_back({y_int, 0});
                }
            }
            
            
            sort(events.begin(), events.end(), [](const pair<Frac,int>& A, const pair<Frac,int>& B){
                return A.first < B.first;
            });

            
            for (size_t i = 0; i + 1 < events.size(); i += 2) {
                intervals.push_back({events[i].first, events[i+1].first});
            }

            
            for (int i = 0; i < n; ++i) {
                Point a = poly[i];
                Point b = poly[(i + 1) % n];
                if (a.x == b.x && a.x * q == p) {
                    long long y_min = min(a.y, b.y);
                    long long y_max = max(a.y, b.y);
                    intervals.push_back({Frac(y_min, 1), Frac(y_max, 1)});
                }
            }

            
            if (intervals.empty()) {
                cout << "0 1" << endl;
                continue;
            }

            sort(intervals.begin(), intervals.end(), [](const pair<Frac,Frac>& A, const pair<Frac,Frac>& B){
                if (A.first == B.first) return A.second < B.second;
                return A.first < B.first;
            });

            vector<pair<Frac, Frac>> merged;
            merged.push_back(intervals[0]);

            for (size_t i = 1; i < intervals.size(); ++i) {
                
                if (intervals[i].first < merged.back().second || intervals[i].first == merged.back().second) {
                    if (merged.back().second < intervals[i].second) {
                        merged.back().second = intervals[i].second;
                    }
                } else {
                    merged.push_back(intervals[i]);
                }
            }

            Frac total_len(0, 1);
            for (auto& seg : merged) {
                total_len = total_len + (seg.second - seg.first);
            }
            cout << total_len.n << " " << total_len.d << endl;
        }
    }
}

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

    
    int T = inf.readInt();
    cout << T << endl;

    while (T--) {
        solve_case();
    }

    finish(_ok, "All cases passed");
}


Adaptive interactor (C++):

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

using namespace std;


struct Point {
    long long x, y;
};



bool are_collinear(Point a, Point b, Point c) {
    long long cp = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
    return cp == 0;
}

int main(int argc, char* argv[]) {
    
    registerGen(argc, argv, 1);

    
    
    string mode = opt<string>("mode", "non");
    
    
    
    int seed_val = atoi(argv[1]);
    int n;
    
    if (seed_val == 1) n = 3;          
    else if (seed_val == 2) n = 4;     
    else if (seed_val == 3) n = 1000;  
    else n = rnd.next(3, 1000);        
    
    
    
    cout << 1 << endl;
    cout << n << endl;

    vector<Point> points;
    
    
    
    
    while (true) {
        points.clear();
        set<pair<long long, long long>> used;
        
        
        while ((int)points.size() < n) {
            long long x = rnd.next(0, 1000);
            long long y = rnd.next(0, 1000);
            if (used.count({x, y})) continue;
            used.insert({x, y});
            points.push_back({x, y});
        }
        
        
        double cx = 0, cy = 0;
        for (const auto& p : points) {
            cx += p.x;
            cy += p.y;
        }
        cx /= n;
        cy /= n;
        
        
        
        sort(points.begin(), points.end(), [&](const Point& a, const Point& b) {
            return atan2(a.y - cy, a.x - cx) < atan2(b.y - cy, b.x - cx);
        });
        
        
        bool ok = true;
        for (int i = 0; i < n; i++) {
            
            if (are_collinear(points[i], points[(i + 1) % n], points[(i + 2) % n])) {
                ok = false;
                break;
            }
        }
        
        if (ok) break;
    }
    
    
    if (mode == "non") {
        
        
        for (const auto& p : points) {
            cout << p.x << " " << p.y << "\n";
        }
    } else {
        
        
        
        shuffle(points.begin(), points.end());
        for (const auto& p : points) {
            cout << p.x << " " << p.y << "\n";
        }
    }
    
    return 0;
}