Problem: icpc2025_rocky_mountain_i
Difficulty: easy
Categories: data structures, search
Time limit: 4000 ms, memory limit: 256 MB

This is an interactive problem. This means that the input to your program will change based on what your program outputs.

Väinö has been selected as the new chairman for the Iconic Character Poetry Contest (ICPC), a televised battle of wits and verse boasting the nation’s best and brightest. Unfortunately, ICPC’s popularity has exploded in recent years, and Väinö is beset with hoards of new applicants wishing to test their mettle.

The program has a limited number of slots to accept applicants, and Väinö is tasked with ensuring that only the best applicants get said slots. He has decided to host a qualifying tournament to decide who advances. In the tournament, he will invite two applicants at a time to compete with each other to assess which has the greater skill at poetry, or runous. He is certain that no two applicants will have the same skill level. The skill levels are transitive, so if $\exists$ defeats b and b defeats $\subset$ , then $\exists$ would also defeat $\subset$ .

However, his time is limited and he wants to limit how many matches he must hold in order to determine the top applicants that will advance. Can you help him choose which matches to hold?

# Interaction

Interaction begins by reading two integers, $N$ and $K$ , representing the total number of applicants and the number that will advance. You are given that $1 \leq N \leq 1 5 0 0 0$ and $1 \le K \le \operatorname* { m i n } ( N , 3 0 )$ . The $N$ applicants are numbered from 1 to $N$ .

You may then make at most $1 2 N + 1 0 0 0$ queries of the form $? \ \mathrm { ~ a ~ } \ \mathrm { ~ b ~ }$ , where $1 \leq a , b \leq N$ and $a \neq b$ . This represents holding a match between applicants $a$ and $b$ , and the interactor will respond with either $a$ or $b$ , representing which applicant won the match and has higher skill.

Remember to flush your output after making a query.

cout.flush(); // $\mathrm { C } + +$ System.out.flush(); // Java print(..., flush ${ . } = { }$ True) # Python

Your submission will receive a Wrong Answer verdict if you make more than $1 2 N + 1 0 0 0$ queries.

Once you have identified the $K$ applicants with highest skill, print them out in the form ! $_ { \textrm { x l } }$ $_ { \textrm { x 2 } }$ x3 ... xK. The top $K$ applicants may be output in any order. Your program should then exit.

The interactor is not adaptive, meaning that the skill level of each applicant is predetermined at the start of the interaction and does not change during the interaction.

Beyond correctly identifying the top $K$ applicants, your queries must also show that your answer is the unique set of $K$ applicants that could be the top $K$ — Väinö’s wrath will befall you if there is any possibility of you missing a budding talent! For example, simply outputting $\ ! 5 3 4$ for the sample will receive Wrong Answer since your queries have not proven that $\{ 5 , 3 , 4 \}$ are the top 3 applicants.

If the interactor receives any invalid input, the interactor will output $- 1$ and then immediately terminate. Your program should cleanly exit in order to receive a Wrong Answer verdict, otherwise the verdict that it receives may be an arbitrary verdict indicating that your submission is incorrect.

You are provided with a command-line tool for local testing. The tool has comments at the top to explain its use.

<table><tr><td colspan="2" rowspan="2">Read                                         Sample Interaction 1                                         Write63</td></tr><tr><td colspan="1" rowspan="1">63</td></tr><tr><td colspan="1" rowspan="1"></td><td colspan="1" rowspan="1">？13</td></tr><tr><td colspan="1" rowspan="1">3</td><td colspan="1" rowspan="1"></td></tr><tr><td colspan="1" rowspan="1"></td><td colspan="1" rowspan="1">？12</td></tr><tr><td colspan="1" rowspan="1">1</td><td colspan="1" rowspan="1"></td></tr><tr><td colspan="1" rowspan="1"></td><td colspan="1" rowspan="1">？35</td></tr><tr><td colspan="1" rowspan="1">5</td><td colspan="1" rowspan="1"></td></tr><tr><td colspan="1" rowspan="1"></td><td colspan="1" rowspan="1">？36</td></tr><tr><td colspan="1" rowspan="1">3</td><td colspan="1" rowspan="1"></td></tr><tr><td colspan="1" rowspan="1"></td><td colspan="1" rowspan="1">？54</td></tr><tr><td colspan="1" rowspan="1">5</td><td colspan="1" rowspan="1"></td></tr><tr><td colspan="1" rowspan="1"></td><td colspan="1" rowspan="1">？34</td></tr><tr><td colspan="1" rowspan="1">4</td><td colspan="1" rowspan="1"></td></tr><tr><td colspan="1" rowspan="1"></td><td colspan="1" rowspan="1">！534</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;
        tout.flush();
    } catch (...) {}
}


template <typename... Args>
static void finish(TResult verdict, const char* fmt, Args... args) {
    log_metrics();
    quitf(verdict, fmt, args...);
}

const int MAXN = 15005;
int skills[MAXN]; 
vector<int> adj[MAXN]; 
int in_degree[MAXN];
bitset<MAXN> anc[MAXN]; 

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

    
    int n = inf.readInt(1, 15000, "n");
    int k = inf.readInt(1, min(n, 30), "k");
    
    
    
    for (int i = 1; i <= n; i++) {
        skills[i] = inf.readInt();
    }

    
    
    query_limit = 12LL * n + 1000;
    
    long long hard_cap = std::max((long long)200000, 2 * query_limit);

    
    log_metrics();

    
    cout << n << " " << k << endl;

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

        
        if (queries > 0 && queries % 1000 == 0) {
            log_metrics();
        }

        
        if (!(cin >> token)) {
            finish(_wa, "Unexpected EOF: expected query or answer");
        }

        if (token == "?") {
            int u, v;
            if (!(cin >> u >> v)) {
                finish(_wa, "Invalid query format: expected two integers");
            }

            
            if (u < 1 || u > n || v < 1 || v > n || u == v) {
                cout << -1 << endl; 
                finish(_wa, "Invalid query arguments: %d %d", u, v);
            }

            queries++;

            
            int winner;
            if (skills[u] > skills[v]) {
                winner = u;
                adj[u].push_back(v); 
                in_degree[v]++;
            } else {
                winner = v;
                adj[v].push_back(u); 
                in_degree[u]++;
            }

            cout << winner << endl;
        } else if (token == "!") {
            
            vector<int> ans;
            set<int> distinct_ans;
            for (int i = 0; i < k; i++) {
                int x;
                if (!(cin >> x)) {
                    finish(_wa, "Invalid answer format: expected K integers");
                }
                if (x < 1 || x > n) {
                    finish(_wa, "Answer index out of range: %d", x);
                }
                ans.push_back(x);
                distinct_ans.insert(x);
            }

            if (distinct_ans.size() != k) {
                finish(_wa, "Answer contains duplicate indices");
            }

            

            
            vector<pair<int, int>> sorted_skills;
            for (int i = 1; i <= n; i++) {
                sorted_skills.push_back({skills[i], i});
            }
            sort(sorted_skills.rbegin(), sorted_skills.rend()); 

            set<int> true_top_k;
            for (int i = 0; i < k; i++) {
                true_top_k.insert(sorted_skills[i].second);
            }

            if (distinct_ans != true_top_k) {
                finish(_wa, "Incorrect set of applicants (not the actual top K)");
            }

            
            
            
            
            
            
            
            vector<int> q;
            int current_in[MAXN];
            for (int i = 1; i <= n; i++) current_in[i] = in_degree[i];
            
            for (int i = 1; i <= n; i++) {
                if (current_in[i] == 0) q.push_back(i);
            }

            int head = 0;
            while(head < (int)q.size()) {
                int u = q[head++];
                
                for (int v : adj[u]) {
                    
                    anc[v] |= anc[u];
                    anc[v].set(u); 
                    
                    current_in[v]--;
                    if (current_in[v] == 0) {
                        q.push_back(v);
                    }
                }
            }

            
            for (int i = 1; i <= n; i++) {
                
                if (distinct_ans.find(i) == distinct_ans.end()) {
                    if ((int)anc[i].count() < k) {
                        finish(_wa, "Uniqueness not proven: candidate %d has only %d known superiors (need %d)", i, (int)anc[i].count(), k);
                    }
                }
            }

            
            if (queries > query_limit) {
                finish(_wa, "Query limit exceeded: %lld > %lld", queries, query_limit);
            }

            finish(_ok, "Correct and proven");

        } else {
            
            cout << -1 << endl;
            finish(_wa, "Invalid protocol token: '%s'", token.c_str());
        }
    }

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

    
    long long seed_val = -1;
    try {
        if (argc > 1) seed_val = stoll(string(argv[1]));
    } catch (...) {
        seed_val = -1;
    }

    
    if (n == -1) {
        if (seed_val == 1) {
            n = 2; k = 1; 
        } else if (seed_val == 2) {
            n = 15000; k = 30; 
        } else if (seed_val == 3) {
            n = 15000; k = 1; 
        } else if (seed_val == 4) {
            n = 30; k = 30; 
        } else if (seed_val == 5) {
            n = 60; k = 30; 
        } else if (seed_val == 6) {
            n = 100; k = 10; 
        } else {
            
            int type = rnd.next(100);
            if (type < 5) n = rnd.next(2, 50);          
            else if (type < 15) n = rnd.next(50, 1000); 
            else n = rnd.next(1000, 15000);             
        }
    }

    
    if (k == -1) {
        int max_k = min(n, 30);
        
        int type = rnd.next(10);
        if (type == 0) k = 1;
        else if (type == 1) k = max_k;
        else k = rnd.next(1, max_k);
    }

    
    n = max(1, min(15000, n));
    k = max(1, min(min(n, 30), k));

    
    cout << n << " " << k << endl;

    
    if (mode == "non") {
        vector<int> p(n);
        iota(p.begin(), p.end(), 1);

        
        bool special = false;
        
        
        if (seed_val == 7) { 
            
            special = true;
        } else if (seed_val == 8) {
            
            reverse(p.begin(), p.end());
            special = true;
        }

        if (!special) {
            
            
            if (rnd.next(20) == 0) {
                
                int swaps = rnd.next(1, max(1, n / 20));
                for (int i = 0; i < swaps; i++) {
                    int a = rnd.next(n);
                    int b = rnd.next(n);
                    swap(p[a], p[b]);
                }
            } else {
                
                shuffle(p.begin(), p.end());
            }
        }

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

    return 0;
}