Problem: icpc2024_asia_hangzhou_i
Difficulty: easy
Categories: graph, search
Time limit: 1000 ms, memory limit: 1024 MB

This is an interactive problem.

Grammy has an undirected cyclic graph of $n$ ( $4 \leq n \leq 1 0 ^ { 9 }$ ) vertices numbered from 1 to $n$ . An undirected cyclic graph is a graph of $n$ vertices and $n$ undirected edges that form one cycle. Specifically, there is a bidirectional edge between vertex $i$ and vertex $( ( i \mathrm { ~ m o d ~ } n ) + 1 \quad$ ) for each $1 \leq i \leq n$ .

Grammy thinks that this graph is too boring, so she secretly chooses a pair of non-adjacent vertices and connects an undirected edge (called a chord) between them, so that the graph now contains $n$ vertices and $( n + 1 )$ edges.

Your task is to guess the position of the chord by making no more than 40 queries. Each query consists of two vertices $x$ and $y$ , and Grammy will tell you the number of edges on the shortest path between the two vertices.

Note that the interactor is non-adaptive, meaning that the position of the chord is pre-determined.

# Input

There are multiple test cases. The first line of the input contains an integer $T$ ( $1 \le T \le 1 0 ^ { 3 }$ ) indicating the number of test cases. For each test case:

The first line contains an integer $n$ ( $4 \leq n \leq 1 0 ^ { 9 }$ ) indicating the number of vertices.

# Interaction Protocol

To ask a query, output one line. First output ? followed by a space, then output two vertices $x$ and $y$ $( 1 \leq x , y \leq n )$ separated by a space. After flushing your output, your program should read a single integer indicating the number of edges on the shortest path between the two vertices.

To guess the position of the chord, output one line. First output ! followed by a space, then output two vertices $u$ and $v$ ( $1 \leq u , v \leq n$ ) separated by a space, indicating that the chord connects vertices $u$ and $v$ . After flushing your output, your program should read a single integer $r$ $\mathrm { ~ \chi ~ } r \in \{ 1 , - 1 \}$ ) indicating the correctness of your guess. If $r = 1$ then your guess is correct, and your program should continue processing the next test case, or exit immediately if there are no more test cases. Otherwise if $r = - 1$ then your guess is incorrect, and your program should exit immediately to receive a Wrong Answer verdict. Note that your guess does not count as a query.

To flush your output, you may use:

• fflush(stdout) (if you use printf) or cout.flush() (if you use cout) in C and $\mathrm { C } + +$ .   
• System.out.flush() in Java.   
• 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></td><td>？15</td></tr><tr><td>2</td><td>？24</td></tr><tr><td>1</td><td></td></tr><tr><td>1</td><td>！42</td></tr><tr><td>4</td><td></td></tr><tr><td>2</td><td>？24</td></tr><tr><td>1</td><td>！13</td></tr></table>

# Note

The graphs in the sample test cases are illustrated as follows:

![](images/0dd972cad3acff0afcab244567ee31223dec8aded4f8fac1463d66d274b098ae.jpg)

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 long long HARD_CAP = 500000;



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







long long dist_cycle(long long n, long long a, long long b) {
    if (a == b) return 0;
    long long d = abs(a - b);
    return min(d, n - d);
}

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

    
    
    int t_cases = inf.readInt();
    
    
    query_limit = (long long)t_cases * 40;
    
    
    log_metrics();

    
    cout << t_cases << endl;

    
    for (int t = 1; t <= t_cases; ++t) {
        
        
        long long n = inf.readLong();
        long long u = inf.readLong();
        long long v = inf.readLong();

        
        cout << n << endl;

        bool solved = false;
        while (true) {
            
            string token;
            if (!(cin >> token)) {
                finish(_wa, "Unexpected EOF (contestant output ended prematurely)");
            }

            
            if (token == "!") {
                long long gu, gv;
                if (!(cin >> gu >> gv)) {
                    finish(_wa, "Unexpected EOF reading answer coordinates");
                }
                
                
                if (gu < 1 || gu > n || gv < 1 || gv > n) {
                    finish(_wa, "Guessed vertices out of range [1, n]");
                }

                
                bool correct = false;
                if ((gu == u && gv == v) || (gu == v && gv == u)) {
                    correct = true;
                }

                if (correct) {
                    cout << 1 << endl; 
                    solved = true;
                    break; 
                } else {
                    cout << -1 << endl; 
                    finish(_wa, "Incorrect guess");
                }
            } 
            
            else {
                long long qx, qy;
                
                
                if (token == "?") {
                    if (!(cin >> qx >> qy)) {
                        finish(_wa, "Unexpected EOF reading query coordinates");
                    }
                } else {
                    
                    try {
                        qx = stoll(token);
                    } catch (...) {
                        finish(_wa, "Invalid token in output (expected integer, '?' or '!')");
                    }
                    if (!(cin >> qy)) {
                        finish(_wa, "Unexpected EOF reading query y coordinate");
                    }
                }

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

                
                if (queries > HARD_CAP) {
                    finish(_pe, "Hard query limit exceeded (potential infinite loop)");
                }

                
                if (qx < 1 || qx > n || qy < 1 || qy > n) {
                    finish(_wa, "Query vertices out of range [1, n]");
                }

                
                
                
                
                
                
                long long d_orig = dist_cycle(n, qx, qy);
                long long d_via_uv = dist_cycle(n, qx, u) + 1 + dist_cycle(n, v, qy);
                long long d_via_vu = dist_cycle(n, qx, v) + 1 + dist_cycle(n, u, qy);

                long long ans = min({d_orig, d_via_uv, d_via_vu});
                
                cout << ans << endl;
            }
        }
        
        if (!solved) {
            finish(_wa, "Logic error: loop terminated without solution");
        }
    }

    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");
    
    long long input_n = opt<long long>("n", 0);
    
    int input_t = opt<int>("t", 10);
    
    
    long long seed = 0;
    if (argc > 1) seed = atoll(argv[1]);

    vector<long long> cases_n;

    
    if (input_n > 0) {
        
        for (int i = 0; i < input_t; ++i) cases_n.push_back(input_n);
    } else if (seed == 1) {
        
        cases_n = {4, 4, 5, 5, 6, 6, 7, 7, 8, 8};
    } else if (seed == 2) {
        
        long long M = 1000000000;
        cases_n = {M, M, M-1, M-1, M-2, M/2, M/2+1, 999999999, 1000000000, 1000000000};
    } else if (seed == 3) {
        
        cases_n = {16, 32, 64, 128, 256, 1024, 65536, 1000000, 500000000, 1000000000};
    } else {
        
        for (int i = 0; i < input_t; ++i) {
            int p = rnd.next(100);
            long long n;
            
            if (p < 5) n = rnd.next(4, 10);                   
            else if (p < 20) n = rnd.next(11, 1000);          
            else if (p < 50) n = rnd.next(1001, 1000000);     
            else if (p < 80) n = rnd.next(1000001, 1000000000); 
            else n = rnd.next(4, 1000000000);                 
            cases_n.push_back(n);
        }
    }

    
    cout << cases_n.size() << endl;

    for (long long n : cases_n) {
        if (mode == "non") {
            
            
            
            
            long long k;
            int type = rnd.next(10);
            if (type < 2) k = 2;              
            else if (type < 4) k = n / 2;     
            else k = rnd.next(2LL, n - 2);    
            
            
            if (k < 2) k = 2;
            if (k > n - 2) k = n - 2;

            long long u = rnd.next(1LL, n);
            
            long long v = (u + k - 1) % n + 1;
            
            
            if (rnd.next(2)) swap(u, v);
            
            cout << n << " " << u << " " << v << endl;
        } else {
            
            
            cout << n << endl;
        }
    }

    return 0;
}