Problem: icpc2022_moscow_regional_l
Difficulty: medium
Categories: greedy, data structures
Time limit: 3000 ms, memory limit: 512 MB


This is an interactive problem.

Bobo had an array $a$ , initially equal to $( 1 , 2 , \ldots , n )$ . He did the following operations with the array.

• For each $i$ from 1 to $n$ in this order, Bobo picked some index $j$ such that $i \leq j \leq \operatorname* { m i n } ( n , i + 2 )$ , and swapped $a _ { i }$ and $a _ { j }$ . Of course, if $i = j$ , then nothing happened after the operation.

Your goal is to determine the final array. You may ask questions of the following type.

• ? i j meaning the question “How do $a _ { i }$ and $a _ { j }$ compare to each other?”. Bobo will respond to this with one symbol $<$ or $>$ , meaning that $a _ { i } < a _ { j }$ or $a _ { i } > a _ { j }$ , respectively.

You may ask no more than $\lfloor 5 n / 3 \rfloor + 5$ questions. After this, you must guess the array.

# Interaction Protocol

First, the interactor prints the number $n$ in a separate line ( $1 \leq n \leq 3 0 0 0 0$ ). Then the solution makes queries, where each query consists of printing $? \ \dot { \textbf { 1 } } \dot { \textbf { ] } }$ on a separate line, where $1 \leq i , j \leq n$ , and $i \neq j$ . After each query the interactor prints one character $<$ or $>$ on a separate line.

After the solution has finished asking questions, it must make a guess. If you think that the array is $( a _ { 1 } , \ldots , a _ { n } )$ , print ! a1 a2 ... $a _ { n }$ on a separate line and terminate.

If your solution makes more than $\lfloor 5 n / 3 \rfloor + 5$ queries, the interactor will finish with the WA verdict. If you do not flush the output after printing a query, you may receive the IL verdict.

Note that the interactor in this task is adaptive, i.e. the array may be generated at the runtime consistently with your questions.

# Example

<table><tr><td>standard input</td><td>standard output</td></tr><tr><td>5</td><td>？54</td></tr><tr><td>&lt;</td><td>？51</td></tr><tr><td>&gt;</td><td>？53</td></tr><tr><td>&gt;</td><td>？31</td></tr><tr><td>&lt;</td><td>？21</td></tr><tr><td>&gt;</td><td>？52</td></tr><tr><td>&gt;</td><td></td></tr><tr><td></td><td>!23154</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 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);

    
    
    
    
    
    int n = inf.readInt();
    vector<int> hidden_a(n);
    for (int i = 0; i < n; i++) {
        hidden_a[i] = inf.readInt();
    }

    
    
    query_limit = (5LL * n) / 3 + 5;
    
    
    log_metrics();

    
    cout << n << endl;

    
    
    
    long long hard_cap = max(200000LL, 10 * query_limit);

    while (true) {
        
        if (queries > hard_cap) {
            finish(_pe, "Hard query limit exceeded (infinite loop protection)");
        }

        
        string token;
        
        if (!(cin >> token)) {
            finish(_pe, "Unexpected EOF: solution terminated without making a guess");
        }

        if (token == "!") {
            
            vector<int> user_a(n);
            for (int i = 0; i < n; i++) {
                if (!(cin >> user_a[i])) {
                    finish(_pe, "Unexpected EOF while reading answer array");
                }
            }

            
            bool correct = true;
            for (int i = 0; i < n; i++) {
                if (user_a[i] != hidden_a[i]) {
                    correct = false;
                    break;
                }
            }

            if (correct) {
                finish(_ok, "Correct guess");
            } else {
                finish(_wa, "Wrong guess");
            }
        } else {
            
            
            int i, j;
            
            if (token == "?") {
                if (!(cin >> i >> j)) {
                    finish(_pe, "Invalid query format (expected: ? i j)");
                }
            } else {
                
                try {
                    size_t pos;
                    i = stoi(token, &pos);
                    if (pos != token.size()) {
                        
                        finish(_pe, "Invalid token '" + token + "' (expected '?' or integer)");
                    }
                } catch (...) {
                    finish(_pe, "Invalid token '" + token + "' (expected '?' or integer)");
                }
                
                
                if (!(cin >> j)) {
                    finish(_pe, "Invalid query format (expected: i j)");
                }
            }

            
            queries++;

            
            if (i < 1 || i > n || j < 1 || j > n) {
                finish(_pe, format("Query indices out of bounds: %d %d (n=%d)", i, j, n));
            }
            if (i == j) {
                finish(_pe, format("Query indices must be distinct: %d %d", i, j));
            }

            
            
            
            if (hidden_a[i - 1] < hidden_a[j - 1]) {
                cout << "<" << endl;
            } else {
                cout << ">" << endl;
            }
            
            
            if (queries % 1000 == 0) {
                log_metrics();
            }
        }
    }

    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 = 0;



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


void validate_indices(int i, int j, int n) {
    if (i < 1 || i > n || j < 1 || j > n || i == j) {
        finish(_pe, format("Invalid indices %d %d", i, j));
    }
}

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

    
    int n = inf.readInt();
    
    
    
    query_limit = (5LL * n) / 3 + 5;
    
    
    
    long long hard_cap = max(200000LL, 10 * query_limit);

    
    vector<int> a(n);
    
    
    
    
    if (!inf.seekEof()) {
        
        for (int i = 0; i < n; i++) {
            a[i] = inf.readInt();
        }
    } else {
        
        
        iota(a.begin(), a.end(), 1);
        for (int i = 0; i < n; i++) {
            
            
            
            int r = min(n - 1, i + 2);
            int j = rnd.next(i, r);
            swap(a[i], a[j]);
        }
    }

    
    cout << n << endl; 
    
    
    log_metrics();

    
    while (true) {
        char type;
        
        if (!(cin >> type)) {
            finish(_pe, "Unexpected EOF (expected '?' or '!')");
        }

        if (type == '?') {
            int u, v;
            if (!(cin >> u >> v)) {
                finish(_pe, "Invalid query format");
            }
            
            validate_indices(u, v, n);
            
            queries++;
            
            if (queries > hard_cap) {
                finish(_pe, "Hard query limit exceeded");
            }

            
            
            int val_u = a[u - 1];
            int val_v = a[v - 1];
            
            if (val_u < val_v) {
                cout << "<" << endl;
            } else {
                cout << ">" << endl;
            }

        } else if (type == '!') {
            
            vector<int> guess(n);
            for (int i = 0; i < n; i++) {
                if (!(cin >> guess[i])) {
                    finish(_pe, "Invalid answer format (expected array)");
                }
            }
            
            
            bool ok = true;
            for (int i = 0; i < n; i++) {
                if (guess[i] != a[i]) {
                    ok = false;
                    break;
                }
            }

            
            
            if (ok) {
                finish(_ok, format("Correct! queries=%lld", queries));
            } else {
                finish(_wa, format("Wrong answer. queries=%lld", queries));
            }

        } else {
            finish(_pe, format("Unknown token '%c'", type));
        }
    }

    return 0;
}


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<int>("n", -1);
    
    
    
    
    
    int strategy = 0; 

    
    if (n == -1) {
        long long seed_val = atoll(argv[1]);
        
        
        if (seed_val == 1) { 
            n = 5; 
        } else if (seed_val == 2) { 
            n = 1; 
        } else if (seed_val == 3) { 
            n = 2; 
        } else if (seed_val == 4) { 
            n = 3; 
        } else if (seed_val == 5) { 
            n = 30000; strategy = 1; 
        } else if (seed_val == 6) { 
            n = 30000; strategy = 2; 
        } else if (seed_val == 7) { 
            n = 30000; 
        } else {
            
            n = rnd.next(1, 30000);
        }
    }

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

    
    vector<int> a(n);
    iota(a.begin(), a.end(), 1); 

    
    for (int i = 1; i <= n; i++) {
        int r = min(n, i + 2);
        int j = -1;
        
        if (strategy == 1) {
            j = i;
        } else if (strategy == 2) {
            j = r;
        } else {
            j = rnd.next(i, r);
        }
        
        
        swap(a[i - 1], a[j - 1]);
    }

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

    return 0;
}