Problem: cf1930_h
Difficulty: medium
Categories: graph, data structures
Time limit: 5000 ms, memory limit: 256 MB

This is an interactive problem.

Alice has a tree $T$ consisting of $n$ nodes, numbered from $1$ to $n$. Alice will show $T$ to Bob. After observing $T$, Bob needs to tell Alice two permutations $p_1$ and $p_2$ of $[1, 2, \ldots, n]$.

Then, Alice will play $q$ rounds of the following game.

* Alice will create an array $a$ that is a permutation of $[0,1,\ldots,n-1]$. The value of node $v$ will be $a_v$.
* Alice will choose two nodes $u$ and $v$ ($1 \leq u, v \leq n$, $u \neq v$) of $T$ and tell them to Bob. Bob will need to find the $\operatorname{MEX}^\dagger$ of the values on the unique simple path between nodes $u$ and $v$.
* To find this value, Bob can ask Alice at most $5$ queries. In each query, Bob should give three integers $t$, $l$ and $r$ to Alice such that $t$ is either $1$ or $2$, and $1 \leq l \leq r \leq n$. Alice will then tell Bob the value equal to\min\_{i=l}^{r} a[p\_{t,i}].

Note that all rounds are independent of each other. In particular, the values of $a$, $u$ and $v$ can be different in different rounds.

Bob is puzzled as he only knows the HLD solution, which requires $O(\log(n))$ queries per round. So he needs your help to win the game.

$^\dagger$ The $\operatorname{MEX}$ (minimum excludant) of a collection of integers $c_1, c_2, \ldots, c_k$ is defined as the smallest non-negative integer $x$ which does not occur in the collection $c$.

Interaction

Each test contains multiple test cases. The first line contains a single integer $t$ ($1 \leq t \leq 10^4$) — the number of test cases. Read it. The description of the test cases follows.

The first line of each test case contains two positive integers $n$ and $q$ ($2 \leq n \leq 10^5$, $1 \leq q \leq 10^4$) — the number of nodes in $T$ and the number of rounds respectively.

The following next $n-1$ lines contains two integers $u$ and $v$ ($1 \leq u, v \leq n$, $u \neq v$) — denoting an edge between nodes $u$ and $v$. It is guaranteed that the given edges form a tree.

It is guaranteed that the sum of $n$ and $q$ over all test cases does not exceed $10^5$ and $10^4$ respectively.

It is also guaranteed that the sum of $n \cdot q$ does not exceed $3 \cdot 10^6$.

The interaction for each test case begins by outputting two permutations $p_1$ and $p_2$ of $[1, 2, \ldots, n]$.

On a new line, output $n$ space-separated distinct integers denoting $p_1$.

In the next line, output $n$ space-separated distinct integers denoting $p_2$.

Alice will start playing the game.

For each round, you must read two integers, $u$ and $v$ ($1 \leq u, v \leq n$, $u \neq v$). You need to find the $\operatorname{MEX}$ of the values on the unique simple path between nodes $u$ and $v$.

To make a query, output "? $t$ $l$ $r$" without quotes, such that $t$ is either $1$ or $2$, and $1 \leq l \leq r \leq n$. Afterwards, you should read a single integer — the answer to your query $\min_{i=l}^{r} a_{p_{t,i}}$. You can make at most $5$ such queries in each round.

If you want to print the answer, output "! $x$" ($1 \leq x, y \leq n$) without quotes. After doing that, read a single integer, which is normally equal to $1$.

If you receive the integer $-1$ instead of a valid reply, it means your program has made an invalid query, exceeded the query limit, or gave an incorrect answer on the previous test case. Your program must terminate immediately to receive a Wrong Answer verdict. Otherwise, you can get an arbitrary verdict because your solution will continue to read from a closed stream.

After printing a query or the answer, do not forget to output the end of the line and flush the output. Otherwise, you will get Idleness limit exceeded. To do this, use:

* fflush(stdout) or cout.flush() in C++;
* System.out.flush() in Java;
* flush(output) in Pascal;
* stdout.flush() in Python;
* see documentation for other languages.

Example

Input

```
1
3 1
1 2
2 3

2 3

1

0

1
```

Output

```
1 2 3
2 1 3

? 1 2 3

? 2 1 3

! 0
```

Note

In the first test, the interaction proceeds as follows.

|  |  |  |
| --- | --- | --- |
| Solution | Jury | Explanation |
|  | 1 | There are 1 test cases. |
|  | 3 1 | The tree $T$ consists of $3$ nodes, and Alice will play for only one round. |
|  | 1 2 | First edge of $T$ |
|  | 2 3 | Second edge of $T$ |
| 1 2 3 |  | The permutation $p_1$ |
| 2 1 3 |  | The permutation $p_2$ |
|  |  | Alice shuffles $a$ to $a=[0,2,1]$ before giving the nodes for the only round. |
|  | 2 3 | Nodes for the round |
| ? 1 2 3 | 1 | $\min(a_{p_{1,2}},a_{p_{1,3}})=\min(a_2,a_3)=1$ |
| ? 2 1 3 | 0 | $\min(a_{p_{2,1}},a_{p_{2,2}},a_{p_{2,3}})=\min(a_2,a_1,a_3)=0$ |
| ! 0 | 1 | Considering the output of queries, it is clear that $\operatorname{MEX}$ is $0$. Since the output is correct, the jury responds with $1$. |

After each test case, make sure to read $1$ or $-1$.

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 << "\n";
        tout << "query_limit=" << query_limit << "\n";
        tout.flush();
    } catch (...) {
        
    }
}

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

struct SegTreeMin {
    int n = 0;      
    int base = 1;   
    vector<int> seg;

    void init(int n_) {
        n = n_;
        base = 1;
        while (base < n) base <<= 1;
        seg.assign(2 * base, INT_MAX);
    }

    
    void buildFrom1Indexed(const vector<int>& arr) {
        for (int i = 0; i < base; ++i) seg[base + i] = INT_MAX;
        for (int i = 1; i <= n; ++i) seg[base + (i - 1)] = arr[i];
        for (int i = base - 1; i >= 1; --i) seg[i] = min(seg[i << 1], seg[i << 1 | 1]);
    }

    int query(int l, int r) const { 
        int L = base + (l - 1);
        int R = base + (r - 1);
        int res = INT_MAX;
        while (L <= R) {
            if (L & 1) res = min(res, seg[L++]);
            if (!(R & 1)) res = min(res, seg[R--]);
            L >>= 1;
            R >>= 1;
        }
        return res;
    }
};

struct TestCaseData {
    int n = 0, q = 0;
    vector<pair<int,int>> edges;
    struct Round {
        int u = 0, v = 0;
        vector<int> a; 
    };
    vector<Round> rounds;
};

struct LCA {
    int n = 0, LOG = 0;
    vector<int> depth;
    vector<vector<int>> up;
    vector<vector<int>> g;

    void init(int n_, const vector<pair<int,int>>& edges) {
        n = n_;
        g.assign(n + 1, {});
        g.reserve(n + 1);
        for (auto [u, v] : edges) {
            g[u].push_back(v);
            g[v].push_back(u);
        }

        LOG = 1;
        while ((1 << LOG) <= n) ++LOG;
        up.assign(LOG, vector<int>(n + 1, 0));
        depth.assign(n + 1, 0);

        vector<int> parent(n + 1, 0);
        queue<int> qq;
        parent[1] = 0;
        depth[1] = 0;
        qq.push(1);

        vector<char> vis(n + 1, 0);
        vis[1] = 1;

        while (!qq.empty()) {
            int v = qq.front();
            qq.pop();
            up[0][v] = parent[v];
            for (int to : g[v]) {
                if (vis[to]) continue;
                vis[to] = 1;
                parent[to] = v;
                depth[to] = depth[v] + 1;
                qq.push(to);
            }
        }

        for (int k = 1; k < LOG; ++k) {
            for (int v = 1; v <= n; ++v) {
                int mid = up[k - 1][v];
                up[k][v] = (mid == 0 ? 0 : up[k - 1][mid]);
            }
        }
    }

    int lca(int a, int b) const {
        if (depth[a] < depth[b]) swap(a, b);
        int diff = depth[a] - depth[b];
        for (int k = 0; k < LOG; ++k) if (diff & (1 << k)) a = up[k][a];
        if (a == b) return a;
        for (int k = LOG - 1; k >= 0; --k) {
            if (up[k][a] != up[k][b]) {
                a = up[k][a];
                b = up[k][b];
            }
        }
        return up[0][a];
    }
};

static bool readIntSafe(long long& x) {
    if (!(cin >> x)) return false;
    return true;
}

static bool readStringSafe(string& s) {
    if (!(cin >> s)) return false;
    return true;
}

static void sendMinusOneAndFinish(TResult verdict, const string& msg) {
    cout << -1 << "\n";
    cout.flush();
    finish(verdict, msg);
}

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

    vector<TestCaseData> tests;
    int T = 0;

    
    try {
        T = inf.readInt(1, 10000, "t");
        tests.resize(T);
        long long totalRounds = 0;

        for (int tc = 0; tc < T; ++tc) {
            int n = inf.readInt(2, 100000, "n");
            int q = inf.readInt(1, 10000, "q");
            tests[tc].n = n;
            tests[tc].q = q;
            tests[tc].edges.reserve(n - 1);
            for (int i = 0; i < n - 1; ++i) {
                int u = inf.readInt(1, n, "u");
                int v = inf.readInt(1, n, "v");
                tests[tc].edges.push_back({u, v});
            }
            tests[tc].rounds.resize(q);
            for (int i = 0; i < q; ++i) {
                int u = inf.readInt(1, n, "u_round");
                int v = inf.readInt(1, n, "v_round");
                tests[tc].rounds[i].u = u;
                tests[tc].rounds[i].v = v;
                tests[tc].rounds[i].a.assign(n + 1, 0);
                for (int node = 1; node <= n; ++node) {
                    tests[tc].rounds[i].a[node] = inf.readInt(0, n - 1, "a");
                }
            }
            totalRounds += q;
        }

        query_limit = 5LL * totalRounds;
    } catch (...) {
        query_limit = 0;
        log_metrics();
        finish(_pe, "Failed to read hidden input");
    }

    
    log_metrics();

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

    
    cout << T << "\n";
    cout.flush();

    for (int tc = 0; tc < T; ++tc) {
        const int n = tests[tc].n;
        const int q = tests[tc].q;

        
        cout << n << " " << q << "\n";
        for (auto [u, v] : tests[tc].edges) {
            cout << u << " " << v << "\n";
        }
        cout.flush();

        
        vector<int> p1(n + 1, 0), p2(n + 1, 0);
        {
            vector<char> used(n + 1, 0);
            for (int i = 1; i <= n; ++i) {
                long long x;
                if (!readIntSafe(x)) sendMinusOneAndFinish(_pe, "EOF while reading p1");
                if (x < 1 || x > n) sendMinusOneAndFinish(_pe, "p1 value out of range");
                if (used[(int)x]) sendMinusOneAndFinish(_pe, "p1 is not a permutation");
                used[(int)x] = 1;
                p1[i] = (int)x;
            }
        }
        {
            vector<char> used(n + 1, 0);
            for (int i = 1; i <= n; ++i) {
                long long x;
                if (!readIntSafe(x)) sendMinusOneAndFinish(_pe, "EOF while reading p2");
                if (x < 1 || x > n) sendMinusOneAndFinish(_pe, "p2 value out of range");
                if (used[(int)x]) sendMinusOneAndFinish(_pe, "p2 is not a permutation");
                used[(int)x] = 1;
                p2[i] = (int)x;
            }
        }

        
        LCA lca;
        lca.init(n, tests[tc].edges);
        const vector<int>& parent = lca.up[0];

        vector<int> b1(n + 1, 0), b2(n + 1, 0);
        SegTreeMin st1, st2;
        st1.init(n);
        st2.init(n);

        vector<int> seen(n + 1, 0);
        int seenStamp = 0;

        for (int round = 0; round < q; ++round) {
            const int u = tests[tc].rounds[round].u;
            const int v = tests[tc].rounds[round].v;
            const vector<int>& a = tests[tc].rounds[round].a;

            
            for (int i = 1; i <= n; ++i) {
                b1[i] = a[p1[i]];
                b2[i] = a[p2[i]];
            }
            st1.buildFrom1Indexed(b1);
            st2.buildFrom1Indexed(b2);

            
            int w = lca.lca(u, v);
            ++seenStamp;
            int x = u;
            while (x != w) {
                int val = a[x];
                if (0 <= val && val <= n) seen[val] = seenStamp;
                x = parent[x];
            }
            {
                int val = a[w];
                if (0 <= val && val <= n) seen[val] = seenStamp;
            }
            x = v;
            while (x != w) {
                int val = a[x];
                if (0 <= val && val <= n) seen[val] = seenStamp;
                x = parent[x];
            }
            int mex = 0;
            while (mex <= n && seen[mex] == seenStamp) ++mex;

            
            cout << u << " " << v << "\n";
            cout.flush();

            
            while (true) {
                string op;
                if (!readStringSafe(op)) sendMinusOneAndFinish(_pe, "EOF while waiting for command");
                if (op == "?") {
                    long long tt, l, r;
                    if (!readIntSafe(tt) || !readIntSafe(l) || !readIntSafe(r))
                        sendMinusOneAndFinish(_pe, "Malformed query");
                    if (!(tt == 1 || tt == 2) || l < 1 || r < 1 || l > r || r > n)
                        sendMinusOneAndFinish(_pe, "Invalid query parameters");

                    ++queries;
                    if (queries > hard_cap) sendMinusOneAndFinish(_pe, "Hard cap exceeded");

                    int ans = (tt == 1) ? st1.query((int)l, (int)r) : st2.query((int)l, (int)r);
                    cout << ans << "\n";
                    cout.flush();
                } else if (op == "!") {
                    long long xans;
                    if (!readIntSafe(xans)) sendMinusOneAndFinish(_pe, "EOF while reading answer");
                    if (xans < 0 || xans > n) sendMinusOneAndFinish(_pe, "Answer out of range");
                    if ((int)xans != mex) {
                        cout << -1 << "\n";
                        cout.flush();
                        finish(_wa, "Wrong mex");
                    }
                    cout << 1 << "\n";
                    cout.flush();
                    break;
                } else {
                    sendMinusOneAndFinish(_pe, "Unexpected command token");
                }
            }
        }
    }

    finish(_ok, "OK");
    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;

static void log_metrics() {
    try {
        tout << "queries=" << queries << "\n";
        tout << "query_limit=" << query_limit << "\n";
        tout.flush();
    } catch (...) {
        
    }
}

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

static bool parseLL(const string& s, long long& out) {
    if (s.empty()) return false;
    int i = 0;
    int sign = 1;
    if (s[0] == '-') {
        sign = -1;
        i = 1;
        if (i == (int)s.size()) return false;
    }
    long long v = 0;
    for (; i < (int)s.size(); ++i) {
        unsigned char c = (unsigned char)s[i];
        if (!isdigit(c)) return false;
        int d = s[i] - '0';
        if (v > (LLONG_MAX - d) / 10) return false;
        v = v * 10 + d;
    }
    out = sign * v;
    return true;
}

static bool readToken(string& tok) {
    return (cin >> tok) ? true : false;
}

static bool readLLFromCin(long long& x) {
    string tok;
    if (!readToken(tok)) return false;
    return parseLL(tok, x);
}

struct LCA {
    int n = 0;
    int LOG = 0;
    vector<vector<int>> g;
    vector<int> depth;
    vector<vector<int>> up;

    void init(int n_, const vector<pair<int,int>>& edges) {
        n = n_;
        g.assign(n + 1, {});
        for (auto [u, v] : edges) {
            if (u < 1 || u > n || v < 1 || v > n) continue;
            g[u].push_back(v);
            g[v].push_back(u);
        }
        LOG = 1;
        while ((1 << LOG) <= n) ++LOG;
        up.assign(LOG, vector<int>(n + 1, 0));
        depth.assign(n + 1, 0);

        vector<int> parent(n + 1, 0);
        queue<int> q;
        q.push(1);
        parent[1] = 0;
        depth[1] = 0;

        vector<char> vis(n + 1, 0);
        vis[1] = 1;

        while (!q.empty()) {
            int v = q.front(); q.pop();
            up[0][v] = parent[v];
            for (int to : g[v]) {
                if (vis[to]) continue;
                vis[to] = 1;
                parent[to] = v;
                depth[to] = depth[v] + 1;
                q.push(to);
            }
        }

        for (int k = 1; k < LOG; ++k) {
            for (int v = 1; v <= n; ++v) {
                int mid = up[k - 1][v];
                up[k][v] = (mid == 0 ? 0 : up[k - 1][mid]);
            }
        }
    }

    int lca(int a, int b) const {
        if (a < 1 || a > n || b < 1 || b > n) return 1;
        if (depth[a] < depth[b]) swap(a, b);
        int diff = depth[a] - depth[b];
        for (int k = 0; k < LOG; ++k) {
            if (diff & (1 << k)) a = up[k][a];
        }
        if (a == b) return a;
        for (int k = LOG - 1; k >= 0; --k) {
            if (up[k][a] != up[k][b]) {
                a = up[k][a];
                b = up[k][b];
            }
        }
        return up[0][a];
    }

    vector<int> getPath(int u, int v) const {
        int w = lca(u, v);
        vector<int> A, B;
        int x = u;
        while (x != w && x != 0) {
            A.push_back(x);
            x = up[0][x];
        }
        A.push_back(w);
        x = v;
        while (x != w && x != 0) {
            B.push_back(x);
            x = up[0][x];
        }
        reverse(B.begin(), B.end());
        A.insert(A.end(), B.begin(), B.end());
        return A;
    }
};

struct TestCaseData {
    int n = 0, q = 0;
    vector<pair<int,int>> edges;
    vector<pair<int,int>> rounds;
    bool hasExplicitA = false;
    vector<vector<int>> aRounds; 
};

struct RoundOracle {
    int n = 0;
    bool hasA = false;
    const vector<int>* a = nullptr; 
    long long mul = 1;
    long long add = 0;

    int val(int node) const {
        if (hasA) return (*a)[node];
        long long x = node - 1;
        long long res = (mul * x + add) % n;
        return (int)res;
    }
};

static bool isPerm1toN(const vector<int>& p, int n) {
    if ((int)p.size() != n + 1) return false;
    vector<char> seen(n + 1, 0);
    for (int i = 1; i <= n; ++i) {
        int x = p[i];
        if (x < 1 || x > n) return false;
        if (seen[x]) return false;
        seen[x] = 1;
    }
    return true;
}

static bool isPerm0toNminus1(const vector<int>& a, int n) {
    if ((int)a.size() != n + 1) return false;
    vector<char> seen(n, 0);
    for (int i = 1; i <= n; ++i) {
        int x = a[i];
        if (x < 0 || x >= n) return false;
        if (seen[x]) return false;
        seen[x] = 1;
    }
    return true;
}

static long long pickCoprimeMultiplier(int n, long long seed) {
    if (n == 1) return 0;
    long long a = (seed % n + n) % n;
    if (a == 0) a = 1;
    for (int it = 0; it < n + 5; ++it) {
        if (a != 0 && std::gcd((long long)n, a) == 1) return a;
        a++;
        if (a == n) a = 1;
    }
    return 1;
}

static int mexOnPath(const LCA& lca, int u, int v, const RoundOracle& oracle) {
    vector<int> path = lca.getPath(u, v);
    int m = (int)path.size();
    vector<char> seen(m + 1, 0); 
    for (int node : path) {
        int x = oracle.val(node);
        if (0 <= x && x <= m) seen[x] = 1;
    }
    for (int x = 0; x <= m; ++x) if (!seen[x]) return x;
    return m;
}

static int minOnSegment(const vector<int>& p, int l, int r, const RoundOracle& oracle) {
    int mn = INT_MAX;
    for (int i = l; i <= r; ++i) {
        int node = p[i];
        mn = min(mn, oracle.val(node));
    }
    return mn;
}

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

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

    vector<TestCaseData> tests;
    tests.reserve(t);

    long long totalQ = 0;

    for (int tc = 0; tc < t; ++tc) {
        TestCaseData data;
        data.n = inf.readInt();
        data.q = inf.readInt();
        if (data.n < 2 || data.n > 100000) finish(_pe, "invalid n in input");
        if (data.q < 1 || data.q > 10000) finish(_pe, "invalid q in input");
        totalQ += data.q;

        data.edges.reserve(data.n - 1);
        for (int i = 0; i < data.n - 1; ++i) {
            int u = inf.readInt();
            int v = inf.readInt();
            data.edges.push_back({u, v});
        }

        if (t == 1) {
            vector<long long> rest;
            while (!inf.seekEof()) {
                rest.push_back(inf.readInt());
            }

            if ((long long)rest.size() == 2LL * data.q) {
                data.rounds.reserve(data.q);
                for (int i = 0; i < data.q; ++i) {
                    int u = (int)rest[2LL * i + 0];
                    int v = (int)rest[2LL * i + 1];
                    data.rounds.push_back({u, v});
                }
                data.hasExplicitA = false;
            } else if ((long long)rest.size() == 1LL * data.q * (2 + data.n)) {
                data.rounds.reserve(data.q);
                data.aRounds.assign(data.q, vector<int>(data.n + 1, 0));
                long long idx = 0;
                for (int i = 0; i < data.q; ++i) {
                    int u = (int)rest[idx++];
                    int v = (int)rest[idx++];
                    data.rounds.push_back({u, v});
                    for (int node = 1; node <= data.n; ++node) {
                        data.aRounds[i][node] = (int)rest[idx++];
                    }
                }
                data.hasExplicitA = true;
            } else if ((long long)rest.size() >= 2LL * data.q) {
                data.rounds.reserve(data.q);
                for (int i = 0; i < data.q; ++i) {
                    int u = (int)rest[2LL * i + 0];
                    int v = (int)rest[2LL * i + 1];
                    data.rounds.push_back({u, v});
                }
                data.hasExplicitA = false;
            } else {
                finish(_pe, "input schema mismatch after edges");
            }
        } else {
            data.rounds.reserve(data.q);
            for (int i = 0; i < data.q; ++i) {
                int u = inf.readInt();
                int v = inf.readInt();
                data.rounds.push_back({u, v});
            }
            data.hasExplicitA = false;
        }

        tests.push_back(std::move(data));
    }

    query_limit = 5LL * totalQ;
    log_metrics(); 

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

    cout << t << "\n";
    cout.flush();

    for (int tc = 0; tc < t; ++tc) {
        const auto& data = tests[tc];
        int n = data.n, q = data.q;

        cout << n << " " << q << "\n";
        for (auto [u, v] : data.edges) cout << u << " " << v << "\n";
        cout.flush();

        vector<int> p1(n + 1, 0), p2(n + 1, 0);
        for (int i = 1; i <= n; ++i) {
            long long x;
            if (!readLLFromCin(x)) finish(_pe, "EOF while reading p1");
            if (x < INT_MIN || x > INT_MAX) finish(_pe, "invalid integer in p1");
            p1[i] = (int)x;
        }
        for (int i = 1; i <= n; ++i) {
            long long x;
            if (!readLLFromCin(x)) finish(_pe, "EOF while reading p2");
            if (x < INT_MIN || x > INT_MAX) finish(_pe, "invalid integer in p2");
            p2[i] = (int)x;
        }
        if (!isPerm1toN(p1, n)) finish(_pe, "p1 is not a permutation");
        if (!isPerm1toN(p2, n)) finish(_pe, "p2 is not a permutation");

        if (data.hasExplicitA) {
            for (int i = 0; i < q; ++i) {
                if (!isPerm0toNminus1(data.aRounds[i], n)) finish(_pe, "explicit a is not a permutation");
            }
        }

        LCA lca;
        lca.init(n, data.edges);

        for (int round = 0; round < q; ++round) {
            int u = data.rounds[round].first;
            int v = data.rounds[round].second;
            if (u < 1 || u > n || v < 1 || v > n || u == v) finish(_pe, "invalid (u,v) in hidden rounds");

            cout << u << " " << v << "\n";
            cout.flush();

            RoundOracle oracle;
            oracle.n = n;

            if (data.hasExplicitA) {
                oracle.hasA = true;
                oracle.a = &data.aRounds[round];
            } else {
                oracle.hasA = false;
                long long seedMul = ( (long long)(u + v) * 1000003LL + 2LL * round + 1 );
                oracle.mul = pickCoprimeMultiplier(n, seedMul);
                oracle.add = ((long long)u * 239017LL + (long long)v * 1000003LL + (long long)round * 97LL) % n;
            }

            while (true) {
                string tok;
                if (!readToken(tok)) finish(_pe, "EOF during interaction");

                if (tok == "?") {
                    long long tt, l, r;
                    if (!readLLFromCin(tt) || !readLLFromCin(l) || !readLLFromCin(r)) finish(_pe, "malformed query");
                    if (!(tt == 1 || tt == 2)) finish(_pe, "query t must be 1 or 2");
                    if (l < 1 || r < 1 || l > r || r > n) finish(_pe, "query range out of bounds");

                    ++queries;
                    if (queries > hard_cap) finish(_pe, "too many queries (hard cap exceeded)");

                    int ans = (tt == 1) ? minOnSegment(p1, (int)l, (int)r, oracle)
                                        : minOnSegment(p2, (int)l, (int)r, oracle);

                    cout << ans << "\n";
                    cout.flush();
                } else if (tok == "!") {
                    long long xll;
                    if (!readLLFromCin(xll)) finish(_pe, "malformed answer");
                    if (xll < 0 || xll > n) finish(_pe, "answer out of bounds");
                    int x = (int)xll;

                    int mex = mexOnPath(lca, u, v, oracle);
                    if (x != mex) finish(_wa, "wrong mex");

                    cout << 1 << "\n";
                    cout.flush();
                    break;
                } else if (tok == "-1") {
                    finish(_pe, "contestant reported -1");
                } else {
                    finish(_pe, "unexpected token");
                }
            }
        }
    }

    finish(_ok, "ok"); 
}


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

static void shuffleVec(vector<int>& v) {
    for (int i = (int)v.size() - 1; i > 0; --i) {
        int j = rnd.next(0, i);
        swap(v[i], v[j]);
    }
}

struct LCA {
    int n = 0;
    int LOG = 0;
    vector<int> depth;
    vector<vector<int>> up;
    vector<vector<int>> g;

    LCA() = default;

    LCA(int n_, const vector<pair<int,int>>& edges) {
        init(n_, edges);
    }

    void init(int n_, const vector<pair<int,int>>& edges) {
        n = n_;
        g.assign(n + 1, {});
        for (auto [u, v] : edges) {
            g[u].push_back(v);
            g[v].push_back(u);
        }

        LOG = 1;
        while ((1 << LOG) <= n) ++LOG;
        up.assign(LOG, vector<int>(n + 1, 0));
        depth.assign(n + 1, 0);

        queue<int> q;
        vector<int> parent(n + 1, 0);
        parent[1] = 0;
        depth[1] = 0;
        q.push(1);

        while (!q.empty()) {
            int v = q.front();
            q.pop();
            up[0][v] = parent[v];
            for (int to : g[v]) {
                if (to == parent[v]) continue;
                parent[to] = v;
                depth[to] = depth[v] + 1;
                q.push(to);
            }
        }

        for (int k = 1; k < LOG; ++k) {
            for (int v = 1; v <= n; ++v) {
                int mid = up[k - 1][v];
                up[k][v] = (mid == 0 ? 0 : up[k - 1][mid]);
            }
        }
    }

    int lca(int a, int b) const {
        if (depth[a] < depth[b]) swap(a, b);
        int diff = depth[a] - depth[b];
        for (int k = 0; k < LOG; ++k) {
            if (diff & (1 << k)) a = up[k][a];
        }
        if (a == b) return a;
        for (int k = LOG - 1; k >= 0; --k) {
            if (up[k][a] != up[k][b]) {
                a = up[k][a];
                b = up[k][b];
            }
        }
        return up[0][a];
    }

    vector<int> getPath(int u, int v) const {
        int w = lca(u, v);
        vector<int> A, B;
        int x = u;
        while (x != w) {
            A.push_back(x);
            x = up[0][x];
        }
        A.push_back(w);
        x = v;
        while (x != w) {
            B.push_back(x);
            x = up[0][x];
        }
        reverse(B.begin(), B.end());
        A.insert(A.end(), B.begin(), B.end());
        return A;
    }
};

static pair<vector<pair<int,int>>, vector<pair<int,int>>> buildTreeAndRoundsCorner(long long seed, string mode) {
    vector<pair<int,int>> edges;
    vector<pair<int,int>> rounds;

    if (seed == 1) {
        int n = 2, q = 1;
        edges = {{1,2}};
        rounds = {{1,2}};
        return {edges, rounds};
    }
    if (seed == 2) {
        int n = 7, q = 3;
        for (int i = 2; i <= n; ++i) edges.push_back({1, i}); 
        rounds = {{2,3}, {4,5}, {6,7}};
        return {edges, rounds};
    }
    
    {
        int n = 10, q = 5;
        for (int i = 1; i <= 6; ++i) edges.push_back({i, i + 1}); 
        edges.push_back({4, 8});
        edges.push_back({4, 9});
        edges.push_back({4, 10});
        rounds = {{8,7}, {9,7}, {10,7}, {8,9}, {1,7}};
        return {edges, rounds};
    }
}

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

    string mode = opt<string>("mode", "non");
    if (mode != "non" && mode != "adp") mode = "non";

    long long seedVal = 0;
    if (argc >= 2) {
        try { seedVal = stoll(string(argv[1])); } catch (...) { seedVal = 0; }
    }

    int nOpt = opt<int>("n", (mode == "adp" ? 60000 : 60000));
    int qOpt = opt<int>("q", (mode == "adp" ? 50 : 50));

    auto clampNQ = [&](int& n, int& q) {
        n = max(2, min(n, 100000));
        q = max(1, min(q, 10000));
        long long prod = 1LL * n * q;
        if (prod > 3000000LL) {
            q = (int)(3000000LL / n);
            q = max(1, q);
        }
    };

    vector<pair<int,int>> edges_old;
    vector<pair<int,int>> rounds_old;
    int n = 0, q = 0;

    if (seedVal >= 1 && seedVal <= 3) {
        auto [edgesC, roundsC] = buildTreeAndRoundsCorner(seedVal, mode);
        edges_old = edgesC;
        rounds_old = roundsC;
        n = 0;
        for (auto [u, v] : edges_old) n = max(n, max(u, v));
        q = (int)rounds_old.size();
    } else {
        n = nOpt;
        q = qOpt;
        clampNQ(n, q);

        int L = n / 2;
        L = max(2, L);
        int hub = max(1, L / 2);

        int S = n / 3;
        S = min(S, n - L);
        S = max(0, S);

        edges_old.reserve(n - 1);
        for (int i = 1; i < L; ++i) edges_old.push_back({i, i + 1}); 

        for (int i = 1; i <= S; ++i) {
            int node = L + i;
            edges_old.push_back({hub, node}); 
        }

        for (int node = L + S + 1; node <= n; ++node) {
            int roll = rnd.next(1, 100);
            int parent = 1;
            if (roll <= 50) parent = rnd.next(1, L);
            else if (roll <= 80) parent = hub;
            else parent = rnd.next(1, node - 1);
            if (parent == node) parent = max(1, node - 1);
            edges_old.push_back({parent, node});
        }

        
        rounds_old.reserve(q);
        auto pickStarLeaf = [&]() -> int {
            if (S == 0) return rnd.next(1, n);
            return L + 1 + rnd.next(0, S - 1);
        };
        auto pickNode = [&]() -> int { return rnd.next(1, n); };

        int endA = 1;
        int endB = L;

        for (int i = 0; i < q; ++i) {
            int u = 1, v = 2;
            int type = i % 10;

            if (type == 0 || type == 1) {
                u = pickStarLeaf();
                v = endB;
            } else if (type == 2) {
                u = endA;
                v = pickStarLeaf();
            } else if (type == 3) {
                u = endA;
                v = endB;
            } else if (type == 4) {
                u = hub;
                v = endB;
            } else if (type == 5) {
                u = pickStarLeaf();
                v = pickStarLeaf();
            } else if (type == 6) {
                u = pickNode();
                v = (rnd.next(0, 1) ? endA : endB);
            } else if (type == 7) {
                u = pickNode();
                v = pickNode();
            } else if (type == 8) {
                u = endB;
                v = pickStarLeaf();
            } else {
                u = pickNode();
                v = pickNode();
            }
            if (u == v) v = (u == 1 ? 2 : 1);
            rounds_old.push_back({u, v});
        }
    }

    
    if (n == 0) {
        n = 0;
        for (auto [u, v] : edges_old) n = max(n, max(u, v));
        for (auto [u, v] : rounds_old) n = max(n, max(u, v));
        n = max(n, 2);
    }
    q = (int)rounds_old.size();

    
    vector<int> perm(n + 1), invperm(n + 1);
    for (int i = 1; i <= n; ++i) perm[i] = i;

    if (!(seedVal >= 1 && seedVal <= 3)) {
        vector<int> labels(n);
        for (int i = 0; i < n; ++i) labels[i] = i + 1;
        shuffleVec(labels);
        for (int i = 1; i <= n; ++i) perm[i] = labels[i - 1];
    }
    for (int i = 1; i <= n; ++i) invperm[perm[i]] = i;

    vector<pair<int,int>> edges_new;
    edges_new.reserve(edges_old.size());
    for (auto [u, v] : edges_old) edges_new.push_back({perm[u], perm[v]});

    vector<pair<int,int>> rounds_new;
    rounds_new.reserve(rounds_old.size());
    for (auto [u, v] : rounds_old) rounds_new.push_back({perm[u], perm[v]});

    
    cout << 1 << "\n";
    cout << n << " " << q << "\n";
    for (auto [u, v] : edges_new) cout << u << " " << v << "\n";

    
    LCA lcaOld(n, edges_old);

    
    
    if (mode == "adp") {
        for (int i = 0; i < q; ++i) {
            cout << rounds_new[i].first << " " << rounds_new[i].second << "\n";
        }
        return 0;
    }

    
    for (int i = 0; i < q; ++i) {
        int uOld = rounds_old[i].first;
        int vOld = rounds_old[i].second;
        int uNew = rounds_new[i].first;
        int vNew = rounds_new[i].second;

        vector<int> path = lcaOld.getPath(uOld, vOld);
        int m = (int)path.size();

        vector<int> aOld(n + 1, -1);
        vector<char> onPath(n + 1, 0);
        for (int x : path) onPath[x] = 1;

        bool makeLargeMex = true;
        if (m < n) {
            int roll = rnd.next(1, 100);
            makeLargeMex = (roll <= 75);
        }

        int startVal = makeLargeMex ? 0 : 1;
        for (int idx = 0; idx < m; ++idx) {
            int val = startVal + idx;
            if (val >= n) val = n - 1; 
            aOld[path[idx]] = val;
        }

        vector<char> usedVal(n, 0);
        for (int v = 1; v <= n; ++v) {
            if (aOld[v] != -1) usedVal[aOld[v]] = 1;
        }

        vector<int> leftover;
        leftover.reserve(n);
        for (int val = 0; val < n; ++val) if (!usedVal[val]) leftover.push_back(val);
        shuffleVec(leftover);

        int ptr = 0;
        for (int node = 1; node <= n; ++node) {
            if (aOld[node] == -1) {
                aOld[node] = leftover[ptr++];
            }
        }

        vector<int> aNew(n + 1, 0);
        for (int oldNode = 1; oldNode <= n; ++oldNode) {
            int newNode = perm[oldNode];
            aNew[newNode] = aOld[oldNode];
        }

        cout << uNew << " " << vNew << "\n";
        for (int node = 1; node <= n; ++node) {
            if (node > 1) cout << " ";
            cout << aNew[node];
        }
        cout << "\n";
    }

    return 0;
}