| #include <iostream> |
| #include <vector> |
| #include <algorithm> |
| #include <utility> |
|
|
| using namespace std; |
|
|
| |
| const int MAXN = 100005; |
| const int LOGN = 18; |
|
|
| |
| int n; |
| long long D[MAXN]; |
| int parent_node[MAXN]; |
| vector<int> adj[MAXN]; |
| int sz[MAXN]; |
| int rep[MAXN]; |
| int up[MAXN][LOGN]; |
|
|
| |
| long long query(int u, int v) { |
| cout << "? " << u << " " << v << endl; |
| long long d; |
| cin >> d; |
| return d; |
| } |
|
|
| |
| void add_node(int u, int p) { |
| parent_node[u] = p; |
| adj[p].push_back(u); |
| sz[u] = 1; |
| rep[u] = u; |
| |
| |
| up[u][0] = p; |
| for (int k = 1; k < LOGN; ++k) { |
| up[u][k] = up[up[u][k-1]][k-1]; |
| } |
| |
| |
| int curr = p; |
| while (curr != 0) { |
| sz[curr]++; |
| rep[curr] = u; |
| curr = parent_node[curr]; |
| } |
| } |
|
|
| |
| int get_ancestor_by_dist(int u, long long target_dist) { |
| if (D[u] == target_dist) return u; |
| for (int k = LOGN - 1; k >= 0; --k) { |
| int anc = up[u][k]; |
| if (anc != 0 && D[anc] >= target_dist) { |
| u = anc; |
| } |
| } |
| return u; |
| } |
|
|
| void solve() { |
| if (!(cin >> n)) return; |
| |
| |
| for (int i = 1; i <= n; ++i) { |
| adj[i].clear(); |
| sz[i] = 0; |
| rep[i] = 0; |
| for (int k = 0; k < LOGN; ++k) up[i][k] = 0; |
| } |
|
|
| if (n == 1) { |
| cout << "! " << endl; |
| return; |
| } |
|
|
| D[1] = 0; |
| vector<pair<long long, int>> nodes; |
| |
| for (int i = 2; i <= n; ++i) { |
| D[i] = query(1, i); |
| nodes.push_back({D[i], i}); |
| } |
|
|
| |
| sort(nodes.begin(), nodes.end()); |
|
|
| |
| sz[1] = 1; |
| rep[1] = 1; |
| |
| for (auto& p : nodes) { |
| int u = p.second; |
| long long d_u = p.first; |
| |
| int curr = 1; |
| |
| |
| while (true) { |
| if (adj[curr].empty()) { |
| add_node(u, curr); |
| break; |
| } |
| |
| |
| sort(adj[curr].begin(), adj[curr].end(), [](int a, int b) { |
| return sz[a] > sz[b]; |
| }); |
| |
| bool found = false; |
| for (int child : adj[curr]) { |
| int l = rep[child]; |
| long long dist_ul = query(u, l); |
| long long dist_lca = (d_u + D[l] - dist_ul) / 2; |
| |
| if (dist_lca == D[curr]) { |
| |
| continue; |
| } else { |
| |
| int w = get_ancestor_by_dist(l, dist_lca); |
| curr = w; |
| found = true; |
| break; |
| } |
| } |
| |
| if (!found) { |
| |
| add_node(u, curr); |
| break; |
| } |
| } |
| } |
|
|
| cout << "!"; |
| for (int i = 2; i <= n; ++i) { |
| int p = parent_node[i]; |
| long long w = D[i] - D[p]; |
| cout << " " << p << " " << i << " " << w; |
| } |
| cout << endl; |
| } |
|
|
| int main() { |
| ios_base::sync_with_stdio(false); |
| cin.tie(NULL); |
| |
| int t; |
| if (cin >> t) { |
| while (t--) { |
| solve(); |
| } |
| } |
| return 0; |
| } |