Problem: icpc2025_asia_wuhan_h
Difficulty: medium
Categories: bit, search
Time limit: 2000 ms, memory limit: 1024 MB

This is an interactive problem.

Little L has a hidden set of size $n$ , $\{ a _ { 1 } , a _ { 2 } , \ldots , a _ { n } \}$ $0 \leq a _ { i } < 2 ^ { 3 0 }$ ), with all elements distinct.

To guess the values of the elements in the set, you can make queries to Little L. In a query, you give a non-negative integer $c$ , and Little $\mathrm { L }$ will tell you $\operatorname* { m a x } \{ a _ { i } \oplus c \}$ , where $\bigoplus$ denotes bitwise XOR $^ \dagger$ .

For example, if $\begin{array} { r l r } { a } & { { } \ = \ } & { \{ 1 , 4 , 6 \} } \end{array}$ and $\begin{array} { r l r } { c } & { { } = } & { 3 } \end{array}$ , Little L will respond 7, because $\operatorname* { m a x } \{ 1 \oplus 3 , 4 \oplus 3 , 6 \oplus 3 \} = \operatorname* { m a x } \{ 2 , 7 , 5 \} = 7$ .

You need to guess all elements of the set using no more than $2 n - 2$ queries.

$^ \dagger$ Bitwise XOR is defined as adding two binary numbers bit by bit modulo 2. For example: $( 0 0 1 1 ) _ { 2 } \oplus$ $( 0 1 0 1 ) _ { 2 } = ( 0 1 1 0 ) _ { 2 }$ .

# Input

Each test file contains multiple test cases. The first line contains the number of test cases $T$ ( $1 \le T \le 1 0 ^ { 4 }$ ).   
The description of the test cases follows.

Each test case contains a single line with a positive integer $n$ ( $2 \leq n \leq 1 0 ^ { 5 }$ ), representing the size of the set.

For each test file, it is guaranteed that the sum of $n$ over all test cases does not exceed $1 0 ^ { 5 }$ .

# Interaction Protocol

To make a query, output in the format “? $c "$ ( $0 \leq c < 2 ^ { 3 0 }$ ). After flushing the output, your program should read a single integer from input, which is the value of $\operatorname* { m a x } \{ a _ { i } \oplus c \}$ . For each test case, you can make at most $2 n - 2$ queries. If you exceed this number of queries, the verdict will be undefined.

To guess the set, output in the format “! a1 a2 . . . ${ a _ { n } } ^ { \prime }$ ( $0 \leq a _ { i } < 2 ^ { 3 0 }$ ). The order of elements can be arbitrary. After flushing the output, the interaction for the current test case ends. You should immediately start handling the next test case, or exit if all test cases have been processed.

Note that the interactor is adaptive. This means that the elements in the set are not fixed at the beginning but may be dynamically adjusted based on your queries. The interactor guarantees that adjustments will not invalidate previous responses.

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.   
• stdout.flush() in Python.

# Example

<table><tr><td>standard input</td><td> standard output</td></tr><tr><td></td><td></td></tr><tr><td>1</td><td>？0</td></tr><tr><td>7號</td><td></td></tr><tr><td>7</td><td>？1</td></tr><tr><td>7號</td><td>？5</td></tr><tr><td>7</td><td>？6</td></tr><tr><td></td><td>!1267</td></tr></table>

Non-adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>
#include <csignal>
using namespace std;

static long long total_queries = 0;
static long long total_query_limit = 0;

static void log_metrics() {
    try {
        tout << "queries=" << total_queries << endl;
        tout << "query_limit=" << total_query_limit << endl;
    } catch (...) {
    }
}

static void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}

struct MaxXorTrie {
    vector<array<int, 2>> child;

    explicit MaxXorTrie(int reserve_values = 0) {
        child.reserve((size_t)reserve_values * 31 + 1);
        child.push_back({-1, -1});
    }

    void insert(int x) {
        int node = 0;
        for (int bit = 29; bit >= 0; bit--) {
            int b = (x >> bit) & 1;
            int& nxt = child[node][b];
            if (nxt == -1) {
                nxt = (int)child.size();
                child.push_back({-1, -1});
            }
            node = nxt;
        }
    }

    int max_xor(int c) const {
        int node = 0;
        int res = 0;
        for (int bit = 29; bit >= 0; bit--) {
            int b = (c >> bit) & 1;
            int prefer = b ^ 1;
            int nxt = child[node][prefer];
            if (nxt != -1) {
                res |= (1 << bit);
                node = nxt;
            } else {
                node = child[node][b];
                if (node == -1) break;
            }
        }
        return res;
    }
};

static uint32_t make_seed(int n, int test_idx) {
    uint32_t x = 0x243f6a88u;
    x ^= (uint32_t)n * 0x9e3779b9u;
    x ^= (uint32_t)test_idx * 0x85ebca6bu;
    return x & ((1u << 30) - 1u);
}

int main(int argc, char** argv) {
    
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    
    registerInteraction(argc, argv);

    
    atexit(log_metrics);
#ifdef SIGPIPE
    signal(SIGPIPE, SIG_IGN);
#endif

    
    int T = inf.readInt();
    if (T < 1 || T > 10000) finish(_fail, "invalid T in hidden input");

    
    cout << T << "\n" << flush;

    for (int t = 1; t <= T; t++) {
        
        int n_raw = inf.readInt();
        if (n_raw < 2 || n_raw > 100000) finish(_fail, "invalid n in hidden input");
        vector<int> a;
        a.reserve(n_raw);
        for (int i = 0; i < n_raw; i++) {
            int x = inf.readInt();
            if (x < 0 || x >= (1 << 30)) finish(_fail, "a_i out of range");
            a.push_back(x);
        }
        
        
        sort(a.begin(), a.end());
        a.erase(unique(a.begin(), a.end()), a.end());
        if ((int)a.size() < n_raw) {
            unordered_set<int> seen;
            seen.reserve((size_t)n_raw * 2);
            for (int x : a) seen.insert(x);

            const uint32_t seed = make_seed(n_raw, t);
            const uint32_t mul = 1103515245u;  
            const uint32_t mask = (1u << 30) - 1u;
            for (uint32_t step = 0; (int)a.size() < n_raw; step++) {
                uint32_t val = (seed + (uint64_t)step * mul) & mask;
                int x = (int)val;
                if (seen.insert(x).second) a.push_back(x);
            }
            sort(a.begin(), a.end());
        }

        MaxXorTrie trie(n_raw);
        for (int x : a) trie.insert(x);

        const long long limit_this = 2LL * n_raw - 2;
        total_query_limit += limit_this;
        log_metrics();

        
        cout << n_raw << "\n" << flush;

        
        long long queries_in_case = 0;
        while (true) {
            string token;
            if (!(cin >> token)) {
                finish(_pe, "Unexpected EOF while reading token");
            }

            if (token == "!") {
                
                vector<int> user_ans(n_raw);
                for (int i = 0; i < n_raw; i++) {
                    if (!(cin >> user_ans[i])) {
                        finish(_pe, "Unexpected EOF while reading answer elements");
                    }
                    if (user_ans[i] < 0 || user_ans[i] >= (1 << 30)) {
                        finish(_pe, "Answer value out of range [0,2^30)");
                    }
                }

                sort(user_ans.begin(), user_ans.end());
                
                if (user_ans == a) {
                    
                    break;
                } else {
                    finish(_wa, "Incorrect set guessed");
                }
            } else {
                
                long long c = 0;
                if (token == "?") {
                    
                    if (!(cin >> c)) finish(_pe, "Unexpected EOF reading query value");
                } else {
                    
                    try {
                        size_t end_idx;
                        c = stoll(token, &end_idx);
                        if (end_idx != token.size()) finish(_pe, "Trailing garbage in query token");
                    } catch (...) {
                        finish(_pe, "Invalid query token (not a number)");
                    }
                }

                
                if (c < 0 || c >= (1LL << 30)) {
                    finish(_pe, "Query value out of range [0, 2^30)");
                }

                
                total_queries++;
                queries_in_case++;
                if (queries_in_case > limit_this) finish(_wa, "Query limit exceeded");
                if (total_queries % 2000 == 0) log_metrics();

                int ans = trie.max_xor((int)c);
                cout << ans << "\n" << flush;
                if (!cout) finish(_pe, "Broken pipe while writing answer");
            }
        }
    }

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


Adaptive interactor (C++):
#include "testlib.h"
#include <bits/stdc++.h>
#include <csignal>

using namespace std;

static long long total_queries = 0;
static long long total_query_limit = 0;

static void log_metrics() {
    try {
        tout << "queries=" << total_queries << endl;
        tout << "query_limit=" << total_query_limit << endl;
    } catch (...) {
    }
}

static void finish(TResult verdict, const string& msg) {
    log_metrics();
    quitf(verdict, "%s", msg.c_str());
}

struct MaxXorTrie {
    vector<array<int, 2>> child;

    explicit MaxXorTrie(int reserve_values = 0) {
        child.reserve((size_t)reserve_values * 31 + 1);
        child.push_back({-1, -1});
    }

    void insert(int x) {
        int node = 0;
        for (int bit = 29; bit >= 0; bit--) {
            int b = (x >> bit) & 1;
            int& nxt = child[node][b];
            if (nxt == -1) {
                nxt = (int)child.size();
                child.push_back({-1, -1});
            }
            node = nxt;
        }
    }

    int max_xor(int c) const {
        int node = 0;
        int res = 0;
        for (int bit = 29; bit >= 0; bit--) {
            int b = (c >> bit) & 1;
            int prefer = b ^ 1;
            int nxt = child[node][prefer];
            if (nxt != -1) {
                res |= (1 << bit);
                node = nxt;
            } else {
                node = child[node][b];
                if (node == -1) break;
            }
        }
        return res;
    }
};

static uint32_t make_seed(int n, int test_idx) {
    uint32_t x = 0x243f6a88u;
    x ^= (uint32_t)n * 0x9e3779b9u;
    x ^= (uint32_t)test_idx * 0x85ebca6bu;
    return x & ((1u << 30) - 1u);
}

int main(int argc, char** argv) {
    registerInteraction(argc, argv);
    atexit(log_metrics);
#ifdef SIGPIPE
    signal(SIGPIPE, SIG_IGN);
#endif

    int t = inf.readInt(1, 10000, "t");
    cout << t << "\n" << flush;

    for (int test_case = 1; test_case <= t; test_case++) {
        int n = inf.readInt(2, 100000, "n");

        const long long limit_this = 2LL * n - 2;
        total_query_limit += limit_this;
        log_metrics();

        
        cout << n << "\n" << flush;

        
        
        
        
        MaxXorTrie trie(n);
        const uint32_t seed = make_seed(n, test_case);
        const uint32_t mul = 1103515245u;  
        const uint32_t mask = (1u << 30) - 1u;
        for (uint32_t i = 0; i < (uint32_t)n; i++) {
            uint32_t val = (seed + (uint64_t)i * mul) & mask;
            trie.insert((int)val);
        }

        vector<pair<int, int>> transcript;
        transcript.reserve((size_t)min<long long>(limit_this, 200000));

        long long queries_in_case = 0;
        while (true) {
            string token;
            if (!(cin >> token)) {
                finish(_pe, "Unexpected end of input from solution");
            }

            if (token == "?") {
                long long c_long;
                if (!(cin >> c_long)) finish(_pe, "Expected integer after '?'");
                if (c_long < 0 || c_long >= (1LL << 30)) finish(_pe, "Query c out of range [0, 2^30)");

                total_queries++;
                queries_in_case++;
                if (queries_in_case > limit_this) finish(_wa, "Query limit exceeded");
                if (total_queries % 2000 == 0) log_metrics();

                const int c = (int)c_long;
                const int ans = trie.max_xor(c);
                transcript.push_back({c, ans});
                cout << ans << "\n" << flush;
                if (!cout) finish(_pe, "Broken pipe while writing answer");

            } else if (token == "!") {
                vector<int> user_set(n);
                for (int i = 0; i < n; i++) {
                    if (!(cin >> user_set[i])) finish(_pe, "EOF while reading answer");
                    if (user_set[i] < 0 || user_set[i] >= (1 << 30)) finish(_pe, "Answer value out of range [0,2^30)");
                }

                sort(user_set.begin(), user_set.end());
                if ((int)user_set.size() != n) finish(_pe, "Invalid answer size");
                for (int i = 1; i < n; i++) {
                    if (user_set[i] == user_set[i - 1]) finish(_wa, "Answer has duplicate elements");
                }

                MaxXorTrie user_trie(n);
                for (int x : user_set) user_trie.insert(x);
                for (auto [c, ans] : transcript) {
                    if (user_trie.max_xor(c) != ans) finish(_wa, "Answer set inconsistent with transcript");
                }

                break;

            } else {
                
                long long c_long = 0;
                try {
                    size_t end_idx;
                    c_long = stoll(token, &end_idx);
                    if (end_idx != token.size()) finish(_pe, "Trailing garbage in query token");
                } catch (...) {
                    finish(_pe, "Expected '?' or '!' or integer c");
                }
                if (c_long < 0 || c_long >= (1LL << 30)) finish(_pe, "Query c out of range [0, 2^30)");

                total_queries++;
                queries_in_case++;
                if (queries_in_case > limit_this) finish(_wa, "Query limit exceeded");
                if (total_queries % 2000 == 0) log_metrics();

                const int c = (int)c_long;
                const int ans = trie.max_xor(c);
                transcript.push_back({c, ans});
                cout << ans << "\n" << flush;
                if (!cout) finish(_pe, "Broken pipe while writing answer");
            }
        }
    }

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


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



















vector<int> generateSet(int n, int type) {
    vector<int> a;
    
    int MAX_VAL = 1073741823;

    if (type == 0) { 
        
        a = rnd.distinct(n, 0, MAX_VAL);
    } 
    else if (type == 1) { 
        
        
        int limit = min((long long)MAX_VAL, (long long)n * 4);
        a = rnd.distinct(n, 0, limit);
    } 
    else if (type == 2) { 
        
        int max_start = MAX_VAL - n;
        int start = rnd.next(0, max_start);
        for (int i = 0; i < n; i++) a.push_back(start + i);
        shuffle(a.begin(), a.end());
    } 
    else if (type == 3) { 
        
        
        
        int min_bits = 0;
        while ((1 << min_bits) < n) min_bits++;
        
        int low_bits = rnd.next(min_bits, 29);
        int high_bits = 30 - low_bits;
        
        int prefix = rnd.next(0, (1 << high_bits) - 1) << low_bits;
        int max_low = (1 << low_bits) - 1;
        
        
        
        if (max_low + 1 >= n) {
            vector<int> low = rnd.distinct(n, 0, max_low);
            for (int x : low) a.push_back(prefix | x);
        } else {
            
            a = rnd.distinct(n, 0, MAX_VAL);
        }
    } 
    else { 
        
        set<int> s;
        while ((int)s.size() < n) {
            int x = 0;
            
            int bits = rnd.next(1, 10);
            for (int k = 0; k < bits; k++) {
                x |= (1 << rnd.next(30));
            }
            if (s.find(x) == s.end()) {
                s.insert(x);
                a.push_back(x);
            }
        }
        
        for (auto x : s) a.push_back(x);
    }
    
    shuffle(a.begin(), a.end());
    return a;
}

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

    string mode = opt<string>("mode", "non");
    
    
    
    long long seed_val = 0;
    try {
        seed_val = stoll(argv[1]);
    } catch (...) {
        seed_val = -1; 
    }

    int t;
    vector<int> ns;
    int max_sum_n = 100000;

    if (seed_val == 1) {
        
        t = 1;
        ns = {2};
    } else if (seed_val == 2) {
        
        t = 1;
        ns = {100000};
    } else if (seed_val == 3) {
        
        t = 10000;
        ns.assign(t, 2); 
    } else {
        
        
        int current_sum = rnd.next(100, max_sum_n);
        
        
        
        
        int limit_t = min(10000, current_sum / 2);
        
        
        if (rnd.next(2) == 0) {
            
            t = rnd.next(1, min(limit_t, 20)); 
        } else {
            
            t = rnd.next(1, limit_t);
        }
        
        
        ns = rnd.partition(t, current_sum, 2);
    }

    
    cout << t << endl;

    
    for (int i = 0; i < t; i++) {
        int n = ns[i];
        cout << n << endl;

        if (mode == "non") {
            
            int type;
            if (seed_val == 1) type = 1; 
            else if (seed_val == 2) type = 0; 
            else if (seed_val > 0 && seed_val <= 10) {
                
                type = rnd.next(0, 4);
            } else {
                
                type = rnd.next(0, 4);
            }

            vector<int> a = generateSet(n, type);
            for (int k = 0; k < n; k++) {
                cout << a[k] << (k == n - 1 ? "" : " ");
            }
            cout << endl;
        } 
        
    }

    return 0;
}