| #include <iostream> |
| #include <vector> |
| #include <algorithm> |
|
|
| using namespace std; |
|
|
| |
| struct Node { |
| int id; |
| long long dist; |
| }; |
|
|
| |
| bool compareNodes(const Node& a, const Node& b) { |
| return a.dist < b.dist; |
| } |
|
|
| int n; |
| vector<long long> dists; |
| vector<int> parent; |
| vector<vector<int>> children; |
| vector<int> sz; |
| vector<Node> sorted_nodes; |
|
|
| |
| long long query(int u, int v) { |
| cout << "? " << u << " " << v << endl; |
| long long d; |
| cin >> d; |
| return d; |
| } |
|
|
| |
| void update_size(int u) { |
| while (u != -1) { |
| sz[u]++; |
| u = parent[u]; |
| } |
| } |
|
|
| |
| int get_ancestor_at_depth(int v, long long target_depth) { |
| int curr = v; |
| while (curr != -1) { |
| if (dists[curr] == target_depth) return curr; |
| |
| |
| if (dists[curr] < target_depth) break; |
| curr = parent[curr]; |
| } |
| return -1; |
| } |
|
|
| void solve() { |
| cin >> n; |
| |
| if (n == 1) { |
| cout << "! " << endl; |
| return; |
| } |
|
|
| |
| dists.assign(n + 1, 0); |
| |
| |
| |
| for (int i = 2; i <= n; ++i) { |
| dists[i] = query(1, i); |
| } |
|
|
| |
| sorted_nodes.resize(n); |
| for (int i = 1; i <= n; ++i) { |
| sorted_nodes[i - 1] = {i, dists[i]}; |
| } |
|
|
| |
| |
| sort(sorted_nodes.begin(), sorted_nodes.end(), compareNodes); |
|
|
| |
| parent.assign(n + 1, -1); |
| children.assign(n + 1, vector<int>()); |
| sz.assign(n + 1, 1); |
|
|
| |
| |
| for (int i = 1; i < n; ++i) { |
| int u = sorted_nodes[i].id; |
| long long du = sorted_nodes[i].dist; |
|
|
| int curr = 1; |
| vector<int> candidates = children[curr]; |
|
|
| while (true) { |
| |
| if (candidates.empty()) { |
| parent[u] = curr; |
| children[curr].push_back(u); |
| update_size(curr); |
| break; |
| } |
|
|
| |
| |
| int best_child = -1; |
| int max_s = -1; |
| int best_idx = -1; |
|
|
| for (size_t k = 0; k < candidates.size(); ++k) { |
| int c = candidates[k]; |
| if (sz[c] > max_s) { |
| max_s = sz[c]; |
| best_child = c; |
| best_idx = k; |
| } |
| } |
|
|
| |
| int v = best_child; |
| while (!children[v].empty()) { |
| int bc = -1; |
| int ms = -1; |
| for (int c : children[v]) { |
| if (sz[c] > ms) { |
| ms = sz[c]; |
| bc = c; |
| } |
| } |
| v = bc; |
| } |
|
|
| |
| long long d_uv = query(u, v); |
| |
| long long d_lca = (du + dists[v] - d_uv) / 2; |
| |
| |
| int lca = get_ancestor_at_depth(v, d_lca); |
|
|
| if (lca == curr) { |
| |
| |
| candidates[best_idx] = candidates.back(); |
| candidates.pop_back(); |
| } else { |
| |
| |
| |
| |
| |
| int child_towards_v = v; |
| if (child_towards_v != lca) { |
| while (parent[child_towards_v] != lca) { |
| child_towards_v = parent[child_towards_v]; |
| } |
| } else { |
| |
| parent[u] = v; |
| children[v].push_back(u); |
| update_size(v); |
| break; |
| } |
|
|
| |
| curr = lca; |
| candidates = children[curr]; |
| |
| |
| for (size_t k = 0; k < candidates.size(); ++k) { |
| if (candidates[k] == child_towards_v) { |
| candidates[k] = candidates.back(); |
| candidates.pop_back(); |
| break; |
| } |
| } |
| } |
| } |
| } |
|
|
| |
| cout << "!"; |
| for (int i = 2; i <= n; ++i) { |
| long long w = dists[i] - dists[parent[i]]; |
| cout << " " << parent[i] << " " << 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; |
| } |