Problem: cf1919_h
Difficulty: hard
Categories: graph, math
Time limit: 2000 ms, memory limit: 256 MB

There is a hidden tree with $n$ vertices. The $n-1$ edges of the tree are numbered from $1$ to $n-1$. You can ask the following queries of two types:

1. Give the grader an array $a$ with $n - 1$ positive integers. For each edge from $1$ to $n - 1$, the weight of edge $i$ is set to $a_i$. Then, the grader will return the length of the diameter$^\dagger$.
2. Give the grader two indices $1 \le a, b \le n - 1$. The grader will return the number of edges between edges $a$ and $b$. In other words, if edge $a$ connects $u_a$ and $v_a$ while edge $b$ connects $u_b$ and $v_b$, the grader will return $\min(\text{dist}(u_a, u_b), \text{dist}(v_a, u_b), \text{dist}(u_a, v_b), \text{dist}(v_a, v_b))$, where $\text{dist}(u, v)$ represents the number of edges on the path between vertices $u$ and $v$.

Find any tree isomorphic$^\ddagger$ to the hidden tree after at most $n$ queries of type 1 and $n$ queries of type 2 in any order.

$^\dagger$ The distance between two vertices is the sum of the weights on the unique simple path that connects them. The diameter is the largest of all those distances.

$^\ddagger$ Two trees, consisting of $n$ vertices each, are called isomorphic if there exists a permutation $p$ containing integers from $1$ to $n$ such that edge ($u$, $v$) is present in the first tree if and only if the edge ($p_u$, $p_v$) is present in the second tree.

Input

The first and only line of input contains a single integer $n$ ($3 \le n \le 1000$) — the number of vertices in the tree.

Interaction

Begin the interaction by reading $n$.

You are allowed to make queries in the following way:

1. "$\mathtt{?}\,1\,a_1\,a_2 \ldots a_{n-1}$" ($1 \le a_i \le 10^9$). Then, you should read an integer $k$ which represents the length of the diameter. You are only allowed to ask this query at most $n$ times.
2. "$\mathtt{?}\,2\,a\,b$" ($1 \le a, b \le n - 1$). Then, you should read an integer $k$ which represents the number of edges between edges $a$ and $b$. You are only allowed to ask this query at most $n$ times.

In case your query is invalid. the program will terminate immediately and you will receive Wrong answer verdict.

To give the final answer, print "!" on a single line, followed by $n - 1$ lines where line $i$ contains "$u_i\,v_i$" ($1 \le u_i, v_i \le n$) which represents that for each $i$ from $1$ to $n-1$, there is an edge between $u_i$ and $v_i$.

After printing a query do not forget to output end of 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

```
5

3

1

9

0
```

Output

```
? 1 1 1 1 1

? 2 1 3

? 1 4 3 2 1

? 2 4 2

!
3 1
4 2
1 2
2 5
```

Note

![](https://espresso.codeforces.com/04e60c463312a091bd51448117d09ed919721314.png)

The hidden tree in the example is shown above. The number on the vertices represents the vertex number while the number on the edges represents the edge number.

![](https://espresso.codeforces.com/4337002a383234649a898b823a2157ac0988b24b.png)

In the first query, all the edges are set to weight $1$, so the diameter has length $3$ as shown in the diagram.

In the second query, there is $1$ edge between edges $1$ and $3$.

![](https://espresso.codeforces.com/eb8a4b5acfb391ae149b45739c703fdf5c8cd363.png)

In the third query, the diameter is $9$ by taking edges $1$, $2$ and $3$.

In the fourth query, there are no edges between edges $4$ and $2$.

![](https://espresso.codeforces.com/012faa895ca4899a44456aef44d56bc6c82424b4.png)

The answer given in the example is shown in the above diagram. Since it is isomorphic to the hidden tree, it is accepted as a correct answer. Note that the edges can be printed in any order.

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

struct DSU {
    vector<int> p, r;
    DSU(int n = 0) { init(n); }
    void init(int n) {
        p.resize(n + 1);
        r.assign(n + 1, 0);
        iota(p.begin(), p.end(), 0);
    }
    int find(int x) { return p[x] == x ? x : p[x] = find(p[x]); }
    bool unite(int a, int b) {
        a = find(a); b = find(b);
        if (a == b) return false;
        if (r[a] < r[b]) swap(a, b);
        p[b] = a;
        if (r[a] == r[b]) r[a]++;
        return true;
    }
};

static vector<int> find_centers(const vector<vector<int>>& adj) {
    int n = (int)adj.size() - 1;
    vector<int> deg(n + 1, 0);
    queue<int> q;
    for (int i = 1; i <= n; i++) {
        deg[i] = (int)adj[i].size();
        if (deg[i] <= 1) q.push(i);
    }
    int remaining = n;
    while (remaining > 2) {
        int sz = (int)q.size();
        if (sz == 0) break;
        remaining -= sz;
        for (int it = 0; it < sz; it++) {
            int u = q.front(); q.pop();
            deg[u] = 0;
            for (int v : adj[u]) {
                if (deg[v] == 0) continue;
                deg[v]--;
                if (deg[v] == 1) q.push(v);
            }
        }
    }
    vector<int> centers;
    while (!q.empty()) { centers.push_back(q.front()); q.pop(); }
    if (centers.empty()) centers.push_back(1);
    sort(centers.begin(), centers.end());
    centers.erase(unique(centers.begin(), centers.end()), centers.end());
    if ((int)centers.size() > 2) centers.resize(2);
    return centers;
}

static string encode_rooted(int u, int p, const vector<vector<int>>& adj) {
    vector<string> subs;
    subs.reserve(adj[u].size());
    for (int v : adj[u]) if (v != p) subs.push_back(encode_rooted(v, u, adj));
    sort(subs.begin(), subs.end());
    string res;
    res.reserve(2 + 2 * subs.size());
    res.push_back('(');
    for (auto& s : subs) res += s;
    res.push_back(')');
    return res;
}

static string canonical_form(const vector<vector<int>>& adj) {
    auto centers = find_centers(adj);
    if ((int)centers.size() == 1) return encode_rooted(centers[0], 0, adj);
    string a = encode_rooted(centers[0], centers[1], adj);
    string b = encode_rooted(centers[1], centers[0], adj);
    
    
    if (a > b) swap(a, b);
    return string("(") + a + b + ")";
}

static void build_lca(const vector<vector<pair<int,int>>>& adj,
                      vector<vector<int>>& up,
                      vector<int>& depth) {
    int n = (int)adj.size() - 1;
    int LOG = (int)up.size();
    depth.assign(n + 1, 0);
    vector<int> parent(n + 1, 0);

    stack<pair<int,int>> st;
    st.push({1, 1});
    parent[1] = 1;
    depth[1] = 0;

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

    while (!st.empty()) {
        auto [u, p] = st.top();
        st.pop();
        parent[u] = p;
        for (auto [v, idx] : adj[u]) {
            (void)idx;
            if (v == p) continue;
            if (vis[v]) continue;
            vis[v] = 1;
            depth[v] = depth[u] + 1;
            st.push({v, u});
        }
    }

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

static int lca(int a, int b, const vector<vector<int>>& up, const vector<int>& depth) {
    if (depth[a] < depth[b]) swap(a, b);
    int LOG = (int)up.size();
    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 int dist_edges(int a, int b, const vector<vector<int>>& up, const vector<int>& depth) {
    int c = lca(a, b, up, depth);
    return depth[a] + depth[b] - 2 * depth[c];
}

static pair<int,long long> farthest_from(int start,
                                        const vector<vector<pair<int,int>>>& adj,
                                        const vector<long long>& w) {
    int n = (int)adj.size() - 1;
    vector<long long> dist(n + 1, LLONG_MIN);
    dist[start] = 0;
    stack<int> st;
    st.push(start);

    while (!st.empty()) {
        int u = st.top();
        st.pop();
        for (auto [v, idx] : adj[u]) {
            if (dist[v] != LLONG_MIN) continue;
            dist[v] = dist[u] + w[idx];
            st.push(v);
        }
    }

    int bestV = start;
    long long bestD = 0;
    for (int i = 1; i <= n; i++) {
        if (dist[i] > bestD) {
            bestD = dist[i];
            bestV = i;
        }
    }
    return {bestV, bestD};
}

static long long weighted_diameter(const vector<vector<pair<int,int>>>& adj,
                                  const vector<long long>& w) {
    auto p1 = farthest_from(1, adj, w);
    auto p2 = farthest_from(p1.first, adj, w);
    return p2.second;
}

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

    int n = inf.readInt();
    if (n < 3 || n > 1000) finish(_pe, "Hidden n out of range");

    vector<int> eu(n), ev(n);
    vector<vector<pair<int,int>>> adjHidden(n + 1);
    vector<vector<int>> adjHiddenUnw(n + 1);

    for (int i = 1; i <= n - 1; i++) {
        int u = inf.readInt();
        int v = inf.readInt();
        if (u < 1 || u > n || v < 1 || v > n || u == v) finish(_pe, "Bad hidden edge");
        eu[i] = u;
        ev[i] = v;
        adjHidden[u].push_back({v, i});
        adjHidden[v].push_back({u, i});
        adjHiddenUnw[u].push_back(v);
        adjHiddenUnw[v].push_back(u);
    }

    query_limit = 2LL * n;
    log_metrics(); 

    
    int LOG = 1;
    while ((1 << LOG) <= n) LOG++;
    vector<vector<int>> up(LOG, vector<int>(n + 1, 1));
    vector<int> depth;
    build_lca(adjHidden, up, depth);

    
    string hiddenCanon = canonical_form(adjHiddenUnw);

    
    cout << n << endl;

    long long hard_cap = max(200000LL, 10LL * query_limit);
    vector<long long> w(n, 1);

    while (true) {
        string cmd;
        if (!(cin >> cmd)) finish(_pe, "EOF before final answer");

        if (cmd == "?") {
            if (++queries > hard_cap) finish(_pe, "Hard cap exceeded");

            int type;
            if (!(cin >> type)) finish(_pe, "Missing query type");

            if (type == 1) {
                for (int i = 1; i <= n - 1; i++) {
                    long long ai;
                    if (!(cin >> ai)) finish(_pe, "Missing weight");
                    if (ai < 1 || ai > 1000000000LL) finish(_pe, "Weight out of range");
                    w[i] = ai;
                }
                long long ans = weighted_diameter(adjHidden, w);
                cout << ans << endl;
            } else if (type == 2) {
                int a, b;
                if (!(cin >> a >> b)) finish(_pe, "Missing edge indices");
                if (a < 1 || a > n - 1 || b < 1 || b > n - 1) finish(_pe, "Edge index out of range");

                int ua = eu[a], va = ev[a];
                int ub = eu[b], vb = ev[b];

                int d1 = dist_edges(ua, ub, up, depth);
                int d2 = dist_edges(ua, vb, up, depth);
                int d3 = dist_edges(va, ub, up, depth);
                int d4 = dist_edges(va, vb, up, depth);
                int ans = min(min(d1, d2), min(d3, d4));
                cout << ans << endl;
            } else {
                finish(_pe, "Unknown query type");
            }
            
        } else if (cmd == "!") {
            vector<vector<int>> adjOut(n + 1);
            DSU dsu(n);
            for (int i = 1; i <= n - 1; i++) {
                int u, v;
                if (!(cin >> u >> v)) finish(_pe, "Missing final edges");
                if (u < 1 || u > n || v < 1 || v > n) finish(_pe, "Final vertex out of range");
                if (u == v) finish(_wa, "Self-loop in final answer");
                adjOut[u].push_back(v);
                adjOut[v].push_back(u);
                if (!dsu.unite(u, v)) finish(_wa, "Cycle in final answer");
            }

            int root = dsu.find(1);
            for (int i = 2; i <= n; i++) {
                if (dsu.find(i) != root) finish(_wa, "Disconnected final answer");
            }

            string outCanon = canonical_form(adjOut);
            if (outCanon != hiddenCanon) finish(_wa, "Final tree is not isomorphic");

            finish(_ok, "OK");
        } else {
            finish(_pe, "Unknown command token");
        }
    }
}


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 DetRng {
    uint64_t x;
    explicit DetRng(uint64_t seed = 88172645463325252ull) : x(seed ? seed : 88172645463325252ull) {}
    uint64_t nextU64() {
        x ^= x >> 12;
        x ^= x << 25;
        x ^= x >> 27;
        return x * 2685821657736338717ull;
    }
    int nextInt(int l, int r) {
        uint64_t span = (uint64_t)(r - l + 1);
        return l + (int)(nextU64() % span);
    }
};

static uint64_t mix64(uint64_t a) {
    a += 0x9e3779b97f4a7c15ull;
    a = (a ^ (a >> 30)) * 0xbf58476d1ce4e5b9ull;
    a = (a ^ (a >> 27)) * 0x94d049bb133111ebull;
    a = a ^ (a >> 31);
    return a;
}

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

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

static bool readIntStrict(int& x) {
    long long t;
    if (!(cin >> t)) return false;
    if (t < INT_MIN || t > INT_MAX) return false;
    x = (int)t;
    return true;
}

static string rooted_encode(int u, int p, const vector<vector<int>>& g) {
    vector<string> child;
    child.reserve(g[u].size());
    for (int v : g[u]) if (v != p) child.push_back(rooted_encode(v, u, g));
    sort(child.begin(), child.end());
    string res;
    res.reserve(2 + 2 * (int)child.size());
    res.push_back('(');
    for (auto& s : child) res += s;
    res.push_back(')');
    return res;
}

static string tree_canonical_form(int n, const vector<pair<int,int>>& edges) {
    vector<vector<int>> g(n + 1);
    g.reserve(n + 1);
    vector<int> deg(n + 1, 0);
    for (auto [u, v] : edges) {
        g[u].push_back(v);
        g[v].push_back(u);
        deg[u]++; deg[v]++;
    }
    if (n == 1) return "()";
    deque<int> q;
    for (int i = 1; i <= n; i++) if (deg[i] <= 1) q.push_back(i);
    int remaining = n;
    while (remaining > 2) {
        int sz = (int)q.size();
        remaining -= sz;
        for (int i = 0; i < sz; i++) {
            int u = q.front(); q.pop_front();
            deg[u] = 0;
            for (int v : g[u]) {
                if (deg[v] > 0) {
                    deg[v]--;
                    if (deg[v] == 1) q.push_back(v);
                }
            }
        }
    }
    vector<int> centers;
    while (!q.empty()) {
        centers.push_back(q.front());
        q.pop_front();
    }
    if (centers.empty()) centers.push_back(1);
    if ((int)centers.size() == 1) {
        return rooted_encode(centers[0], 0, g);
    } else {
        string a = rooted_encode(centers[0], 0, g);
        string b = rooted_encode(centers[1], 0, g);
        return min(a, b);
    }
}

struct Candidate {
    int n = 0;
    int m = 0;
    vector<pair<int,int>> edges;          
    vector<int> eu, ev;                   
    vector<vector<pair<int,int>>> adj;    
    int LOG = 0;
    vector<int> depth;
    vector<vector<int>> up;
    string canon;

    void build(int n_, const vector<pair<int,int>>& edges0) {
        n = n_;
        edges = edges0;
        m = (int)edges.size();
        eu.assign(m + 1, 0);
        ev.assign(m + 1, 0);
        adj.assign(n + 1, {});
        for (int i = 1; i <= m; i++) {
            int u = edges[i - 1].first;
            int v = edges[i - 1].second;
            eu[i] = u; ev[i] = v;
            adj[u].push_back({v, i});
            adj[v].push_back({u, i});
        }
        LOG = 1;
        while ((1 << LOG) <= n) LOG++;
        depth.assign(n + 1, 0);
        up.assign(LOG, vector<int>(n + 1, 0));

        vector<int> parent(n + 1, 0);
        vector<int> st;
        st.reserve(n);
        st.push_back(1);
        parent[1] = 1;
        depth[1] = 0;
        while (!st.empty()) {
            int u = st.back();
            st.pop_back();
            up[0][u] = parent[u];
            for (auto [v, eid] : adj[u]) {
                if (v == parent[u]) continue;
                parent[v] = u;
                depth[v] = depth[u] + 1;
                st.push_back(v);
            }
        }
        for (int k = 1; k < LOG; k++) {
            for (int v = 1; v <= n; v++) {
                up[k][v] = up[k - 1][ up[k - 1][v] ];
            }
        }
        canon = tree_canonical_form(n, edges);
    }

    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];
    }

    int distV(int a, int b) const {
        int c = lca(a, b);
        return depth[a] + depth[b] - 2 * depth[c];
    }

    int edgeEdgeDist(int ea, int eb) const {
        int a1 = eu[ea], a2 = ev[ea];
        int b1 = eu[eb], b2 = ev[eb];
        int d = distV(a1, b1);
        d = min(d, distV(a1, b2));
        d = min(d, distV(a2, b1));
        d = min(d, distV(a2, b2));
        return d;
    }

    pair<int, long long> farthestFrom(int src, const vector<long long>& w) const {
        vector<long long> dist(n + 1, LLONG_MIN / 4);
        vector<int> parent(n + 1, 0);
        vector<int> st;
        st.reserve(n);
        dist[src] = 0;
        parent[src] = src;
        st.push_back(src);
        while (!st.empty()) {
            int u = st.back();
            st.pop_back();
            for (auto [v, eid] : adj[u]) {
                if (v == parent[u]) continue;
                parent[v] = u;
                dist[v] = dist[u] + w[eid];
                st.push_back(v);
            }
        }
        int bestV = src;
        long long bestD = dist[src];
        for (int i = 1; i <= n; i++) {
            if (dist[i] > bestD) {
                bestD = dist[i];
                bestV = i;
            }
        }
        return {bestV, bestD};
    }

    long long diameter(const vector<long long>& w) const {
        auto p1 = farthestFrom(1, w);
        auto p2 = farthestFrom(p1.first, w);
        return p2.second;
    }
};

static vector<pair<int,int>> makePath(int n) {
    vector<pair<int,int>> e;
    e.reserve(max(0, n - 1));
    for (int i = 1; i + 1 <= n; i++) e.push_back({i, i + 1});
    return e;
}

static vector<pair<int,int>> makeStar(int n) {
    vector<pair<int,int>> e;
    e.reserve(max(0, n - 1));
    for (int i = 2; i <= n; i++) e.push_back({1, i});
    return e;
}

static vector<pair<int,int>> makeCompleteBinary(int n) {
    vector<pair<int,int>> e;
    e.reserve(max(0, n - 1));
    for (int i = 2; i <= n; i++) e.push_back({i / 2, i});
    return e;
}

static void buildFixedBinarySubtree(vector<pair<int,int>>& e, int start, int sz) {
    for (int i = 1; i < sz; i++) {
        int v = start + i;
        int parent = start + (i - 1) / 2;
        e.push_back({parent, v});
    }
}

static vector<pair<int,int>> makeSymmetricTwoSubtrees(int n) {
    vector<pair<int,int>> e;
    e.reserve(n - 1);
    int root = 1;
    int m = (n - 1) / 2;
    int s1 = 2;
    int s2 = 2 + m;

    e.push_back({root, s1});
    e.push_back({root, s2});
    buildFixedBinarySubtree(e, s1, m);
    buildFixedBinarySubtree(e, s2, m);

    if (n % 2 == 0) {
        int leftover = 2 + 2 * m;
        if (leftover <= n) e.push_back({root, leftover});
    }
    if ((int)e.size() != n - 1) return makePath(n);
    return e;
}

static vector<pair<int,int>> makeDoubleStarWithBridge(int n, int bridgeLenEdges) {
    vector<pair<int,int>> e;
    e.reserve(n - 1);
    if (n == 3) return makePath(n);
    bridgeLenEdges = max(1, min(bridgeLenEdges, n - 2));

    int centerA = 1;
    int nextNode = 2;
    int last = centerA;

    for (int i = 0; i < bridgeLenEdges - 1; i++) {
        int cur = nextNode++;
        e.push_back({last, cur});
        last = cur;
    }
    int centerB = nextNode++;
    e.push_back({last, centerB});

    int used = nextNode - 1;
    int remaining = n - used;
    int leavesA = remaining / 2;
    int leavesB = remaining - leavesA;

    for (int i = 0; i < leavesA; i++) e.push_back({centerA, nextNode++});
    for (int i = 0; i < leavesB; i++) e.push_back({centerB, nextNode++});

    while ((int)e.size() < n - 1 && nextNode <= n) {
        e.push_back({(int)e.size() % 2 ? centerA : centerB, nextNode++});
    }
    if ((int)e.size() != n - 1) return makePath(n);
    return e;
}

static vector<pair<int,int>> makeSpiderEqualArms(int n, int arms) {
    vector<pair<int,int>> e;
    e.reserve(n - 1);
    int center = 1;
    int k = max(1, min(arms, n - 1));
    int total = n - 1;
    int base = total / k;
    int rem = total % k;

    int nextNode = 2;
    for (int a = 0; a < k; a++) {
        int len = base + (a < rem ? 1 : 0);
        int last = center;
        for (int i = 0; i < len; i++) {
            int cur = nextNode++;
            e.push_back({last, cur});
            last = cur;
        }
    }
    if ((int)e.size() != n - 1) return makePath(n);
    return e;
}

static vector<pair<int,int>> makeCaterpillar(int n, int spineLen) {
    vector<pair<int,int>> e;
    e.reserve(n - 1);
    spineLen = max(2, min(spineLen, n));
    for (int i = 1; i < spineLen; i++) e.push_back({i, i + 1});
    int nextNode = spineLen + 1;
    int attach = 1;
    while (nextNode <= n) {
        e.push_back({attach, nextNode});
        nextNode++;
        attach++;
        if (attach > spineLen) attach = 1;
    }
    if ((int)e.size() != n - 1) return makePath(n);
    return e;
}

static vector<pair<int,int>> makeRandomTreePruferDet(int n, uint64_t seed) {
    vector<pair<int,int>> e;
    e.reserve(n - 1);
    if (n <= 1) return e;
    if (n == 2) return vector<pair<int,int>>{{1, 2}};
    DetRng rng(mix64(seed));
    vector<int> prufer(n - 2);
    for (int i = 0; i < n - 2; i++) prufer[i] = rng.nextInt(1, n);

    vector<int> deg(n + 1, 1);
    for (int x : prufer) deg[x]++;

    priority_queue<int, vector<int>, greater<int>> pq;
    for (int i = 1; i <= n; i++) if (deg[i] == 1) pq.push(i);

    for (int x : prufer) {
        int leaf = pq.top(); pq.pop();
        e.push_back({leaf, x});
        deg[leaf]--;
        deg[x]--;
        if (deg[x] == 1) pq.push(x);
    }
    int a = pq.top(); pq.pop();
    int b = pq.top(); pq.pop();
    e.push_back({a, b});
    if ((int)e.size() != n - 1) return makePath(n);
    return e;
}

static void shuffleEdgesDet(vector<pair<int,int>>& e, uint64_t seed) {
    DetRng rng(mix64(seed));
    for (int i = 0; i < (int)e.size(); i++) {
        int j = rng.nextInt(i, (int)e.size() - 1);
        swap(e[i], e[j]);
    }
}

static vector<Candidate> buildCandidates(int n, const vector<pair<int,int>>* fixedEdges) {
    vector<Candidate> cand;
    if (fixedEdges) {
        Candidate c;
        c.build(n, *fixedEdges);
        cand.push_back(std::move(c));
        return cand;
    }

    struct Shape { vector<pair<int,int>> edges; int variants; uint64_t salt; };
    vector<Shape> shapes;

    shapes.push_back({makePath(n), 3, 0x1111});
    shapes.push_back({makeStar(n), 3, 0x2222});
    shapes.push_back({makeCompleteBinary(n), 3, 0x3333});
    shapes.push_back({makeSymmetricTwoSubtrees(n), 3, 0x4444});
    shapes.push_back({makeCaterpillar(n, max(2, n / 3)), 3, 0x5555});
    shapes.push_back({makeCaterpillar(n, max(2, n / 2)), 3, 0x6666});
    shapes.push_back({makeCaterpillar(n, max(2, (2 * n) / 3)), 3, 0x7777});
    shapes.push_back({makeSpiderEqualArms(n, 3), 3, 0x8888});
    shapes.push_back({makeSpiderEqualArms(n, 5), 3, 0x9999});
    shapes.push_back({makeSpiderEqualArms(n, min(12, max(2, n - 1))), 3, 0xaaaa});
    for (int bl : {1, 2, 3, 5, 10, 20, 37}) {
        if (bl <= n - 2) shapes.push_back({makeDoubleStarWithBridge(n, bl), 3, (uint64_t)0xbbbb + (uint64_t)bl});
    }
    for (int s = 1; s <= 10; s++) {
        shapes.push_back({makeRandomTreePruferDet(n, 0xdeadbeefULL ^ (uint64_t)s), 1, 0xcccc + (uint64_t)s});
    }

    cand.reserve(80);
    for (int si = 0; si < (int)shapes.size(); si++) {
        auto base = shapes[si].edges;
        if ((int)base.size() != n - 1) base = makePath(n);
        for (int v = 0; v < shapes[si].variants; v++) {
            auto e = base;
            uint64_t seed = mix64((uint64_t)n * 1315423911ULL ^ (uint64_t)si * 97531ULL ^ (uint64_t)v * 1234567ULL ^ shapes[si].salt);
            shuffleEdgesDet(e, seed);
            Candidate c;
            c.build(n, e);
            cand.push_back(std::move(c));
        }
    }

    
    
    if ((int)cand.size() > 120) cand.resize(120);
    return cand;
}

static bool validateTreeEdges(int n, const vector<pair<int,int>>& edges, string& why) {
    if ((int)edges.size() != n - 1) {
        why = "wrong number of edges";
        return false;
    }
    vector<int> p(n + 1), r(n + 1, 0);
    iota(p.begin(), p.end(), 0);
    function<int(int)> findp = [&](int x) { return p[x] == x ? x : p[x] = findp(p[x]); };
    auto unite = [&](int a, int b) -> bool {
        a = findp(a); b = findp(b);
        if (a == b) return false;
        if (r[a] < r[b]) swap(a, b);
        p[b] = a;
        if (r[a] == r[b]) r[a]++;
        return true;
    };
    for (auto [u, v] : edges) {
        if (u < 1 || u > n || v < 1 || v > n) {
            why = "vertex out of range";
            return false;
        }
        if (u == v) {
            why = "self-loop";
            return false;
        }
        if (!unite(u, v)) {
            why = "cycle detected";
            return false;
        }
    }
    int root = findp(1);
    for (int i = 2; i <= n; i++) {
        if (findp(i) != root) {
            why = "disconnected";
            return false;
        }
    }
    return true;
}

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

    int n = inf.readInt(3, 1000, "n");

    bool hasFixed = !inf.seekEof();
    vector<pair<int,int>> fixedEdges;
    if (hasFixed) {
        fixedEdges.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");
            fixedEdges.push_back({u, v});
        }
        
    }

    query_limit = 2LL * n;
    log_metrics(); 

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

    vector<Candidate> candidates = buildCandidates(n, hasFixed ? &fixedEdges : nullptr);
    if (candidates.empty()) finish(_pe, "internal: no candidates");

    vector<int> active;
    active.reserve(candidates.size());
    for (int i = 0; i < (int)candidates.size(); i++) active.push_back(i);

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

    int m = n - 1;
    vector<long long> w(m + 1, 1);

    while (true) {
        string tok;
        if (!readToken(tok)) finish(_wa, "unexpected EOF from contestant");

        if (tok == "?") {
            int type;
            if (!readIntStrict(type)) {
                if (cin.eof()) finish(_wa, "unexpected EOF after '?'");
                finish(_pe, "bad query type token");
            }
            queries++;
            if (queries > hard_cap) finish(_pe, "too many queries (hard cap)");

            if (type == 1) {
                for (int i = 1; i <= m; i++) {
                    long long ai;
                    if (!readLongLong(ai)) {
                        if (cin.eof()) finish(_wa, "unexpected EOF in type1 weights");
                        finish(_pe, "bad weight token");
                    }
                    if (ai <= 0 || ai > 1000000000LL) finish(_pe, "weight out of range");
                    w[i] = ai;
                }

                map<long long, vector<int>> buckets;
                for (int id : active) {
                    long long ans = candidates[id].diameter(w);
                    buckets[ans].push_back(id);
                }
                if (buckets.empty()) finish(_pe, "internal: empty buckets");

                long long bestAns = buckets.begin()->first;
                size_t bestSz = buckets.begin()->second.size();
                for (auto& kv : buckets) {
                    if (kv.second.size() > bestSz) {
                        bestSz = kv.second.size();
                        bestAns = kv.first;
                    } else if (kv.second.size() == bestSz && kv.first < bestAns) {
                        bestAns = kv.first;
                    }
                }
                active = std::move(buckets[bestAns]);

                cout << bestAns << "\n";
                cout.flush();
            } else if (type == 2) {
                int a, b;
                if (!readIntStrict(a) || !readIntStrict(b)) {
                    if (cin.eof()) finish(_wa, "unexpected EOF in type2");
                    finish(_pe, "bad indices in type2");
                }
                if (a < 1 || a > m || b < 1 || b > m) finish(_pe, "edge index out of range");

                map<long long, vector<int>> buckets;
                for (int id : active) {
                    long long ans = candidates[id].edgeEdgeDist(a, b);
                    buckets[ans].push_back(id);
                }
                if (buckets.empty()) finish(_pe, "internal: empty buckets");

                long long bestAns = buckets.begin()->first;
                size_t bestSz = buckets.begin()->second.size();
                for (auto& kv : buckets) {
                    if (kv.second.size() > bestSz) {
                        bestSz = kv.second.size();
                        bestAns = kv.first;
                    } else if (kv.second.size() == bestSz && kv.first < bestAns) {
                        bestAns = kv.first;
                    }
                }
                active = std::move(buckets[bestAns]);

                cout << bestAns << "\n";
                cout.flush();
            } else {
                finish(_pe, "unknown query type");
            }
        } else if (tok == "!") {
            vector<pair<int,int>> outEdges;
            outEdges.reserve(n - 1);
            for (int i = 0; i < n - 1; i++) {
                int u, v;
                if (!readIntStrict(u) || !readIntStrict(v)) {
                    if (cin.eof()) finish(_wa, "unexpected EOF in final answer");
                    finish(_pe, "bad final edge token");
                }
                outEdges.push_back({u, v});
            }

            string why;
            if (!validateTreeEdges(n, outEdges, why)) finish(_wa, string("final answer is not a tree: ") + why);

            string canonOut = tree_canonical_form(n, outEdges);
            for (int id : active) {
                if (candidates[id].canon != canonOut) {
                    finish(_wa, "final tree not isomorphic to all feasible hidden instances");
                }
            }
            finish(_ok, "ok");
        } else {
            finish(_pe, "unexpected token (expected '?' or '!')");
        }
    }
}


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

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

static void shuffleVertices(vector<pair<int,int>>& e, int n) {
    vector<int> p(n + 1);
    for (int i = 1; i <= n; i++) p[i] = i;
    for (int i = 1; i <= n; i++) {
        int j = rnd.next(i, n);
        swap(p[i], p[j]);
    }
    for (auto& ed : e) {
        ed.first = p[ed.first];
        ed.second = p[ed.second];
        if (ed.first == ed.second) {
            ed.second = p[1]; 
            if (ed.first == ed.second) ed.second = (ed.first % n) + 1;
        }
    }
}

static vector<pair<int,int>> makePath(int n) {
    vector<pair<int,int>> e;
    e.reserve(max(0, n - 1));
    for (int i = 1; i + 1 <= n; i++) e.push_back({i, i + 1});
    return e;
}

static void buildFixedBinarySubtree(vector<pair<int,int>>& e, int start, int sz) {
    
    for (int i = 1; i < sz; i++) {
        int v = start + i;
        int parent = start + (i - 1) / 2;
        e.push_back({parent, v});
    }
}

static vector<pair<int,int>> makeSymmetricTwoSubtrees(int n) {
    vector<pair<int,int>> e;
    e.reserve(n - 1);
    int root = 1;
    int m = (n - 1) / 2;
    int s1 = 2;
    int s2 = 2 + m;

    e.push_back({root, s1});
    e.push_back({root, s2});
    buildFixedBinarySubtree(e, s1, m);
    buildFixedBinarySubtree(e, s2, m);

    if (n % 2 == 0) {
        int leftover = 2 + 2 * m;
        if (leftover <= n) e.push_back({root, leftover});
    }
    if ((int)e.size() != n - 1) {
        
        return makePath(n);
    }
    return e;
}

static vector<pair<int,int>> makeDoubleStarWithBridge(int n, int bridgeLenEdges) {
    vector<pair<int,int>> e;
    e.reserve(n - 1);

    if (n == 3) return makePath(n);
    bridgeLenEdges = max(1, min(bridgeLenEdges, n - 2));

    int centerA = 1;
    int nextNode = 2;
    int last = centerA;

    for (int i = 0; i < bridgeLenEdges - 1; i++) {
        int cur = nextNode++;
        e.push_back({last, cur});
        last = cur;
    }
    int centerB = nextNode++;
    e.push_back({last, centerB});

    int used = nextNode - 1;
    int remaining = n - used;
    int leavesA = remaining / 2;
    int leavesB = remaining - leavesA;

    for (int i = 0; i < leavesA; i++) {
        e.push_back({centerA, nextNode++});
    }
    for (int i = 0; i < leavesB; i++) {
        e.push_back({centerB, nextNode++});
    }

    if ((int)e.size() != n - 1) {
        
        while ((int)e.size() < n - 1 && nextNode <= n) {
            int to = nextNode++;
            e.push_back({(rnd.next(0, 1) ? centerA : centerB), to});
        }
    }
    if ((int)e.size() != n - 1) return makePath(n);
    return e;
}

static vector<pair<int,int>> makeSpiderEqualArms(int n, int arms) {
    vector<pair<int,int>> e;
    e.reserve(n - 1);
    int center = 1;
    int maxArms = max(1, min(arms, n - 1));
    int total = n - 1;
    int base = total / maxArms;
    int rem = total % maxArms;

    int nextNode = 2;
    for (int a = 0; a < maxArms; a++) {
        int len = base + (a < rem ? 1 : 0);
        int last = center;
        for (int i = 0; i < len; i++) {
            int cur = nextNode++;
            e.push_back({last, cur});
            last = cur;
        }
    }
    if ((int)e.size() != n - 1) return makePath(n);
    return e;
}

static vector<pair<int,int>> makeCaterpillar(int n, int spineLen) {
    vector<pair<int,int>> e;
    e.reserve(n - 1);
    spineLen = max(2, min(spineLen, n));
    for (int i = 1; i < spineLen; i++) e.push_back({i, i + 1});
    int nextNode = spineLen + 1;
    while (nextNode <= n) {
        int attach = rnd.next(1, spineLen);
        e.push_back({attach, nextNode});
        nextNode++;
    }
    if ((int)e.size() != n - 1) return makePath(n);
    return e;
}

static vector<pair<int,int>> makeRandomTreePrufer(int n) {
    vector<pair<int,int>> e;
    e.reserve(n - 1);
    if (n <= 1) return e;
    if (n == 2) { e.push_back({1, 2}); return e; }

    vector<int> prufer(n - 2);
    for (int i = 0; i < n - 2; i++) prufer[i] = rnd.next(1, n);

    vector<int> deg(n + 1, 1);
    for (int x : prufer) deg[x]++;

    priority_queue<int, vector<int>, greater<int>> pq;
    for (int i = 1; i <= n; i++) if (deg[i] == 1) pq.push(i);

    for (int x : prufer) {
        int leaf = pq.top(); pq.pop();
        e.push_back({leaf, x});
        deg[leaf]--;
        deg[x]--;
        if (deg[x] == 1) pq.push(x);
    }

    int a = pq.top(); pq.pop();
    int b = pq.top(); pq.pop();
    e.push_back({a, b});

    if ((int)e.size() != n - 1) return makePath(n);
    return e;
}

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

    string mode = opt<string>("mode", "non");
    int nOpt = opt<int>("n", 1000);
    string pattern = opt<string>("pattern", "auto");
    int arms = opt<int>("arms", 9);

    long long seed = 1;
    if (argc > 1) {
        seed = atoll(argv[1]);
        if (seed <= 0) seed = 1;
    }

    int n = nOpt;
    if (seed == 1) n = 3;                      
    else if (seed == 2) n = max(3, nOpt);      
    else if (seed == 3) n = max(3, nOpt);      
    else {
        n = max(3, nOpt);
        if (n >= 50) {
            int lo = max(3, n - 50);
            int hi = n;
            n = rnd.next(lo, hi);
        }
    }

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

    vector<pair<int,int>> edges;
    if (seed == 1) {
        edges = makePath(n);
    } else if (seed == 2) {
        int bridge = min(n - 2, 37);
        edges = makeDoubleStarWithBridge(n, bridge);
    } else if (seed == 3) {
        edges = makeSpiderEqualArms(n, min(arms, 12));
    } else {
        if (pattern == "auto") {
            int t = rnd.next(0, 4);
            if (t == 0) {
                int bridge = rnd.next(10, min(80, n - 2));
                edges = makeDoubleStarWithBridge(n, bridge);
            } else if (t == 1) {
                edges = makeSymmetricTwoSubtrees(n);
            } else if (t == 2) {
                int spineLen = rnd.next(max(2, n / 3), max(2, (2 * n) / 3));
                edges = makeCaterpillar(n, spineLen);
            } else if (t == 3) {
                edges = makeSpiderEqualArms(n, min(arms, max(2, n - 1)));
            } else {
                edges = makeRandomTreePrufer(n);
            }
        } else if (pattern == "double") {
            int bridge = rnd.next(10, min(80, n - 2));
            edges = makeDoubleStarWithBridge(n, bridge);
        } else if (pattern == "sym") {
            edges = makeSymmetricTwoSubtrees(n);
        } else if (pattern == "cater") {
            int spineLen = rnd.next(max(2, n / 3), max(2, (2 * n) / 3));
            edges = makeCaterpillar(n, spineLen);
        } else if (pattern == "spider") {
            edges = makeSpiderEqualArms(n, min(arms, max(2, n - 1)));
        } else if (pattern == "rand") {
            edges = makeRandomTreePrufer(n);
        } else {
            edges = makeRandomTreePrufer(n);
        }
    }

    if ((int)edges.size() != n - 1) {
        edges = makePath(n);
    }

    shuffleEdges(edges);
    if (rnd.next(0, 1)) shuffleVertices(edges, n);
    shuffleEdges(edges);

    cout << n << "\n";
    for (auto [u, v] : edges) {
        cout << u << " " << v << "\n";
    }
    return 0;
}