Problem: icpc2024_bapc_e
Difficulty: medium
Categories: search, math
Time limit: 1000 ms, memory limit: 256 MB

After years of planning and construction, you finally succeeded in making your own spacecraft! Immediately hopping aboard, you take the spaceship on a maiden voyage to the Big Anthropomorphic Pig Constellation. After a couple of years, you reach the constellation and land on the nearest planet to take some pictures and hopefully score some souvenirs. While haggling with a local souvenir seller using the built-in translator of your spacecraft, you are suddenly notified that the craft is low on fuel! Your extensive use of the translator has drained more fuel than expected, and you cannot get back to Earth. Frantically, you look for a refuelling station. Luckily, a local points you to a shady store, strikingly similar to the petrol stations you know from home.

![](images/b5252c04ee6fc2b3d4ea3a874c18fff1300914c2841f2665cc66154f8268b6bb.jpg)  
The inspiration for your spaceship. CC BY-SA 3.0 by Edwtie on Wikimedia Commons

While the outside of the refuelling station may look similar to those on Earth, the inside is completely different. On a long shelf are a number of fuel canisters, with strange symbols on the side. From your research on rocket fuel, you deduce that these symbols probably denote the oxydilation level of the fuel in the canister. None of the rocket fuels burn on their own. Instead, combining two rocket fuels with oxydilation levels $o _ { x }$ and $o _ { y }$ yields a fuel with burn time $\sqrt { \left| o _ { x } - o _ { y } \right| }$ . You can afford to buy three full canisters, and the burn time of rocket fuel is additive, so that combining rocket fuels with oxydilation levels $o _ { x }$ , $o _ { y }$ and $o _ { z }$ results in a total burn time of

$$
\sqrt { | o _ { x } - o _ { y } | } + \sqrt { | o _ { y } - o _ { z } | } + \sqrt { | o _ { z } - o _ { x } | } .
$$

You can only decode the symbols denoting the oxydilation levels with the translator of your spacecraft. Unfortunately, due to the low fuel levels of the spacecraft, you can only use your translator for 50 items, before the fuel fully runs out. Luckily, you know that rocket fuel is always stored in non-decreasing order of oxydilation levels. Can you figure out which three canisters of rocket fuel to buy to maximize total burn time?

# Interaction

This is an interactive problem. Your submission will be run against an interactor, which reads from the standard output of your submission and writes to the standard input of your submission. This interaction needs to follow a specific protocol:

The interactor first sends one line with an integer $n$ ( $3 \leq n \leq 2 \cdot 1 0 ^ { 5 }$ ), the number of types of rocket fuel.

Then, your program should make at most 50 queries to find the optimal three fuel canisters. Each query is made by printing one line of the form $\begin{array} { r l } { ^ { 6 6 } ? } & { { } i ^ { 9 } } \end{array}$ ( $1 \leq i \leq n$ ). The interactor will respond with one line with an integer $o _ { i }$ ( $\left| o _ { i } \right| \le 1 0 ^ { 6 }$ ), the oxydilation value of the fuel in the $_ i$ th canister.

When you have determined the optimal three distinct canisters $x$ , $y$ , and $z$ ( $1 \leq x , y , z \leq n$ , $x \neq y$ , $x \neq z$ , $y \ne z$ ), print one line of the form “! $x$ y $z ^ { \dag }$ , after which the interaction will stop. Printing the answer does not count as a query.

If there are multiple valid solutions, you may output any one of them.

The interactor is not adaptive: the oxydilation levels of the fuel canisters are fixed up front, and do not depend on your queries.

Make sure you flush the buffer after each write.

A testing tool is provided to help you develop your solution.

Using more than 50 queries will result in a wrong answer.

![](images/397fef922c4051b2c900399c2f7116c17ad3d9fc1d5e366138f23e57fa18c18a.jpg)

![](images/23b44cc66da72db561b69dcbbb72d414a1022948389afb3dfef824340d1ed4f4.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 = 50;  
static const long long HARD_CAP = 2000; 





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

    
    
    int n = inf.readInt(3, 200000, "n");
    vector<int> a(n + 1); 
    for (int i = 1; i <= n; i++) {
        a[i] = inf.readInt(-1000000, 1000000, format("a[%d]", i).c_str());
    }

    
    
    
    
    
    
    
    
    double max_score = -1.0;
    double base_term = sqrt((double)abs(a[n] - a[1])); 

    if (n == 3) {
        
        max_score = base_term + sqrt((double)abs(a[2] - a[1])) + sqrt((double)abs(a[n] - a[2]));
    } else {
        
        for (int j = 2; j < n; j++) {
            double current_score = base_term + sqrt((double)abs(a[j] - a[1])) + sqrt((double)abs(a[n] - a[j]));
            if (current_score > max_score) {
                max_score = current_score;
            }
        }
    }

    
    cout << n << endl; 

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

        string token;
        if (!(cin >> token)) {
            finish(_wa, "Unexpected EOF: contestant did not provide an answer.");
        }

        if (token == "!") {
            
            int x, y, z;
            if (!(cin >> x >> y >> z)) {
                finish(_pe, "Malformed answer: expected 3 integers.");
            }

            
            if (x < 1 || x > n || y < 1 || y > n || z < 1 || z > n) {
                finish(_wa, format("Indices (%d, %d, %d) out of bounds [1, %d].", x, y, z, n));
            }
            if (x == y || x == z || y == z) {
                finish(_wa, "Indices must be distinct.");
            }

            
            double user_score = sqrt((double)abs(a[x] - a[y])) + 
                                sqrt((double)abs(a[y] - a[z])) + 
                                sqrt((double)abs(a[z] - a[x]));
            
            
            if (user_score >= max_score - 1e-7) {
                finish(_ok, format("Correct! Score: %.9f", user_score));
            } else {
                finish(_wa, format("Suboptimal solution. User: %.9f, Optimal: %.9f", user_score, max_score));
            }
        } else {
            
            queries++;
            
            
            
            
            int idx;
            bool valid = false;
            
            
            if (token == "?") {
                if (cin >> idx) valid = true;
            } else {
                try {
                    size_t pos;
                    idx = stoi(token, &pos);
                    if (pos == token.size()) valid = true;
                } catch (...) {
                    valid = false;
                }
            }

            if (!valid) {
                finish(_pe, format("Invalid query format. Token: '%s'", token.c_str()));
            }

            if (idx < 1 || idx > n) {
                finish(_wa, format("Query index %d out of bounds [1, %d].", idx, n));
            }

            
            cout << a[idx] << endl;
        }
    }

    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 n_opt = opt<int>("n", -1);
    
    
    int seed_int = 0;
    if (argc > 1) {
        try {
            seed_int = stoi(string(argv[1]));
        } catch (...) {
            seed_int = 0;
        }
    }

    const int MIN_VAL = -1000000;
    const int MAX_VAL = 1000000;
    const int LIMIT_N = 200000;

    
    int n = n_opt;
    if (n == -1) {
        
        if (seed_int >= 1 && seed_int <= 5) {
            if (seed_int == 1) n = 3;
            else if (seed_int == 2) n = 4;
            else if (seed_int == 3) n = 10;
            else if (seed_int == 4) n = 50; 
            else n = LIMIT_N; 
        } else {
            
            int t = rnd.next(10);
            if (t < 2) n = rnd.next(3, 100);
            else if (t < 4) n = rnd.next(100, 5000);
            else n = rnd.next(LIMIT_N - 20000, LIMIT_N);
        }
    }

    
    
    if (mode == "adp") {
        cout << n << endl;
        cout << MIN_VAL << " " << MAX_VAL << endl;
        return 0;
    }

    
    
    vector<int> a(n);
    int type = 0;

    
    if (seed_int >= 1 && seed_int <= 6) type = seed_int;
    else type = rnd.next(1, 8);

    if (type == 1) { 
        
        for (int i = 0; i < n; i++) a[i] = rnd.next(MIN_VAL, MAX_VAL);
    } else if (type == 2) { 
        
        
        for (int i = 0; i < n; i++) {
            if (rnd.next(2)) a[i] = rnd.next(MIN_VAL, MIN_VAL + 2000);
            else a[i] = rnd.next(MAX_VAL - 2000, MAX_VAL);
        }
    } else if (type == 3) { 
        
        int mid = (MIN_VAL + MAX_VAL) / 2;
        a[0] = mid;
        for (int i = 1; i < n; i++) {
            if (rnd.next(2)) a[i] = rnd.next(MIN_VAL, MIN_VAL + 5000);
            else a[i] = rnd.next(MAX_VAL - 5000, MAX_VAL);
        }
    } else if (type == 4) { 
        
        for (int i = 0; i < n; i++) {
            double p = rnd.next(0.0, 1.0);
            p = p * p * p;
            if (rnd.next(2)) a[i] = MIN_VAL + (int)(p * (MAX_VAL - MIN_VAL));
            else a[i] = MAX_VAL - (int)(p * (MAX_VAL - MIN_VAL));
        }
    } else if (type == 5) { 
        
        int k = rnd.next(3, 10);
        vector<int> centers(k);
        for(int &c : centers) c = rnd.next(MIN_VAL, MAX_VAL);
        for(int i = 0; i < n; i++) {
            a[i] = centers[rnd.next(k)] + rnd.next(-500, 500);
            a[i] = max(MIN_VAL, min(MAX_VAL, a[i]));
        }
    } else if (type == 6) { 
        
        long long step = (long long)(MAX_VAL - MIN_VAL) / (n > 1 ? n - 1 : 1);
        if (step == 0) step = 1;
        long long cur = MIN_VAL;
        for(int i=0; i<n; i++) {
            a[i] = (int)min((long long)MAX_VAL, cur);
            cur += step;
            if (rnd.next(5) == 0) cur += rnd.next(-10, 10); 
        }
    } else { 
        
        for (int i = 0; i < n; i++) a[i] = rnd.next(MIN_VAL, MAX_VAL);
    }
    
    
    if (n == 3 && type == 1) {
        a[0] = MIN_VAL; a[1] = 0; a[2] = MAX_VAL;
    }

    
    for (int& x : a) x = max(MIN_VAL, min(MAX_VAL, x));

    
    sort(a.begin(), a.end());

    
    cout << n << endl;
    for (int i = 0; i < n; i++) {
        cout << a[i] << (i == n - 1 ? "" : " ");
    }
    cout << endl;

    return 0;
}