Problem: icpc2024_nwerc_h
Difficulty: hard
Categories: math
Time limit: 1000 ms, memory limit: 256 MB

For security reasons, TU Delft is going to place locks with numeric keypads on the doors of a large number of rooms. Each room will have its own pass code. The task of setting up the server on which all codes will be stored is given to Harry and Sharon.

Having paid attention in cybersecurity class, they know that the codes should be passed through a hash function, preferably multiple times, before storing.

Sharon came up with the nifty idea of letting the room number be the number of times the code is passed through the hash function. That way, even if two rooms happen to have the same pass code, they will not (necessarily) end up with the same hash value. However, they find that for some combinations of room number and code, the hash value happens to be the same as the original code, presenting a security risk.

Not to be outdone by Sharon, Harry came up with an idea of his own: to switch the roles, that is, let the code be the number of times the hash function is applied to the room number. In other words, if $c$ is the code and $r$ is the room number, the hash value will be $f ^ { c } ( r ) = f ( \cdot \cdot \cdot f ( r ) \cdot \cdot \cdot )$ . | {z }c times

After some thought, Sharon claimed that, regardless of what the function $f$ is, it would always still be the case for some room numbers and codes that the hash value is the same as the code; that is, that $f ^ { c } ( r ) = c$ . In fact, Sharon thinks it would not be too difficult to find two such numbers, even without knowing the full details of $f$ .

![](images/bcd1cf821dfc39b537f6cac4b79379a2a494956d1fa38951ab4473bb05241bfe.jpg)  
Silhouettes free to use from pxfuel.com

This dismissive statement made Harry angry, who believed that Sharon was just jealous of his idea. After a big argument that led nowhere, Harry decided to make Sharon prove her claim: he has written a program that, upon sending it a query, will return the hash value $f ^ { c } ( r )$ for the $c$ and $r$ given in the query, using a secret hash function $f$ he has chosen. The hash function accepts any $r$ in $\{ 1 , \ldots , n \}$ , where $n$ is given, and returns a value in the same range. The value of $c$ should also be in the same range. The challenge for Sharon is to find $c$ and $r$ such that $f ^ { c } ( r ) = c$ , using a limited number of queries.

You know that Sharon is right about her claim and decide to help her.

In the first sample case, the value of $n$ is 6, and the hidden function is given by $f ( 1 ) = 4$ , $f ( 2 ) = 3$ , $f ( 3 ) = 5$ , $f ( 4 ) = 2$ , $f ( 5 ) = 4$ , and $f ( 6 ) = 6$ . In the second sample, $n = 4$ , and $f ( 1 ) = 2$ , $f ( 2 ) = 4$ , $f ( 3 ) = 2$ , and $f ( 4 ) = 2$ .

# Interaction

This is an interactive problem. Your submission will be run against an interactor, which reads 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$ $( 1 \leq n \leq 2 \cdot 1 0 ^ { 5 } )$ ), indicating that the domain of the hidden function $f$ is $\{ 1 , \ldots , n \}$ .

Then, your program should make at most 1000 queries to find the answer. Each query is made by printing one line of the form “? c r” $( 1 \leq c , r \leq n )$ . The interactor will respond with an integer $h$ $( 1 \leq h \leq n )$ , the value of $f ^ { c } ( r )$ .

When you have determined some values of $c$ and $r$ such that $f ^ { c } ( r ) = c$ , print one line of the form “! c $r ^ { , , }$ $( 1 \leq c , r \leq n )$ , 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 hidden function $f$ is fixed up front, and does 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 1000 queries will result in a wrong answer.

<table><tr><td>Read</td><td>Sample Interaction 1</td><td>Write</td></tr><tr><td>6</td><td></td><td></td></tr><tr><td></td><td>？24</td><td></td></tr><tr><td>3</td><td></td><td></td></tr><tr><td></td><td>？41</td><td></td></tr><tr><td>5</td><td></td><td></td></tr><tr><td></td><td>？55</td><td></td></tr><tr><td>4</td><td></td><td></td></tr><tr><td></td><td>？16</td><td></td></tr><tr><td>6</td><td></td><td></td></tr><tr><td></td><td>？21</td><td></td></tr><tr><td></td><td></td><td></td></tr><tr><td>2</td><td>！21</td><td></td></tr></table>

# Sample Interaction 2

Write

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

using namespace std;

static int queries = 0;
static constexpr int QUERY_LIMIT = 1000;

static void log_queries() {
    tout << "queries=" << queries << '\n';
    tout << "query_limit=" << QUERY_LIMIT << '\n';
}

[[noreturn]] static void quit_with(TResult verdict, const string& msg) {
    log_queries();
    quitf(verdict, "%s", msg.c_str());
}

static int jump(const vector<vector<int>>& up, int x, int steps) {
    int cur = x;
    for (int k = 0; k < (int)up.size(); k++) {
        if (steps & (1 << k)) {
            cur = up[k][cur];
        }
    }
    return cur;
}

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

    const int n = inf.readInt(1, 200000, "n");
    vector<int> f(n + 1);
    for (int i = 1; i <= n; i++) {
        f[i] = inf.readInt(1, n, "f");
    }

    int LOG = 0;
    while ((1LL << LOG) <= n) {
        LOG++;
    }
    LOG = max(LOG, 1);

    vector<vector<int>> up(LOG, vector<int>(n + 1));
    for (int i = 1; i <= n; i++) {
        up[0][i] = f[i];
    }
    for (int k = 1; k < LOG; k++) {
        for (int i = 1; i <= n; i++) {
            up[k][i] = up[k - 1][up[k - 1][i]];
        }
    }

    cout << n << endl;

    while (true) {
        string cmd;
        if (!(cin >> cmd)) {
            quit_with(_pe, "Unexpected EOF");
        }

        if (cmd == "?") {
            int c = 0;
            int r = 0;
            if (!(cin >> c >> r)) {
                quit_with(_pe, "Invalid query");
            }
            if (c < 1 || c > n || r < 1 || r > n) {
                quit_with(_pe, "Query out of range");
            }

            queries++;
            if (queries > QUERY_LIMIT) {
                quit_with(_wa, "Too many queries");
            }

            cout << jump(up, r, c) << endl;
            continue;
        }

        if (cmd == "!") {
            int c = 0;
            int r = 0;
            if (!(cin >> c >> r)) {
                quit_with(_pe, "Invalid answer");
            }
            if (c < 1 || c > n || r < 1 || r > n) {
                quit_with(_pe, "Answer out of range");
            }

            const int h = jump(up, r, c);
            if (h == c) {
                quit_with(_ok, "Correct");
            }
            quit_with(_wa, "Wrong answer");
        }

        quit_with(_pe, "Unknown command: " + cmd);
    }
}


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 s_val = 0;
    string seed_str = argv[1];
    bool is_num = true;
    for (char c : seed_str) if (!isdigit(c)) is_num = false;
    if (is_num && !seed_str.empty()) {
        try { s_val = stoll(seed_str); } catch (...) { s_val = 0; }
    }

    int n;
    
    if (opt<int>("n", 0) != 0) {
        n = opt<int>("n");
    } else {
        
        if (s_val == 1) n = 6;      
        else if (s_val == 2) n = 4; 
        else if (s_val >= 3 && s_val <= 6) n = 200000; 
        else {
            
            if (rnd.next(10) == 0) n = rnd.next(1, 100); 
            else n = rnd.next(100000, 200000);           
        }
    }

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

    
    vector<int> f(n + 1);

    if (s_val == 1) {
        
        vector<int> vals = {0, 4, 3, 5, 2, 4, 6};
        if (n == 6) f = vals;
        else {
            
            for (int i = 1; i <= n; ++i) f[i] = vals[(i - 1) % 6 + 1];
        }
    } else if (s_val == 2) {
        
        vector<int> vals = {0, 2, 4, 2, 2};
        if (n == 4) f = vals;
        else {
            for (int i = 1; i <= n; ++i) f[i] = vals[(i - 1) % 4 + 1];
        }
    } else {
        
        int type = 0;
        if (s_val == 3) type = 1;      
        else if (s_val == 4) type = 2; 
        else if (s_val == 5) type = 3; 
        else if (s_val == 6) type = 4; 
        else type = rnd.next(1, 4);    

        if (type == 1) {
            
            vector<int> p(n);
            iota(p.begin(), p.end(), 1);
            shuffle(p.begin(), p.end());
            for (int i = 0; i < n; ++i) {
                f[p[i]] = p[(i + 1) % n];
            }
        } else if (type == 2) {
            
            
            int max_cycle = max(1, (int)sqrt(n) * 10);
            int min_cycle = min(100, max_cycle);
            int cycle_len = rnd.next(min_cycle, max_cycle);
            if (cycle_len > n) cycle_len = n;
            if (cycle_len < 1) cycle_len = 1;
            
            int path_len = n - cycle_len;
            
            vector<int> p(n);
            iota(p.begin(), p.end(), 1);
            shuffle(p.begin(), p.end());
            
            
            for (int i = 0; i < path_len; ++i) {
                f[p[i]] = p[i + 1];
            }
            
            
            for (int i = path_len; i < n; ++i) {
                int next_idx = path_len + (i - path_len + 1) % cycle_len;
                f[p[i]] = p[next_idx];
            }
        } else if (type == 3) {
            
            for (int i = 1; i <= n; ++i) {
                f[i] = rnd.next(1, n);
            }
        } else if (type == 4) {
            
            vector<int> p(n);
            iota(p.begin(), p.end(), 1);
            shuffle(p.begin(), p.end());
            
            int cur = 0;
            while (cur < n) {
                int rem = n - cur;
                int len;
                if (rem <= 5) len = rem;
                else len = rnd.next(2, min(rem, 50000));
                
                for (int i = 0; i < len; ++i) {
                    f[p[cur + i]] = p[cur + (i + 1) % len];
                }
                cur += len;
            }
        }
    }

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

    return 0;
}