| #include <iostream> |
| #include <vector> |
| #include <algorithm> |
|
|
| using namespace std; |
|
|
| |
| long long query(int u, int v) { |
| cout << "? " << u << " " << v << endl; |
| long long d; |
| cin >> d; |
| return d; |
| } |
|
|
| const int MAXN = 100005; |
| long long dist_root[MAXN]; |
| int parent_node[MAXN]; |
| int sz[MAXN]; |
| int heavy_child[MAXN]; |
| vector<int> children[MAXN]; |
|
|
| struct Edge { |
| int u, v; |
| long long w; |
| }; |
| vector<Edge> result_edges; |
|
|
| |
| void add_node(int p, int u, long long w) { |
| parent_node[u] = p; |
| children[p].push_back(u); |
| sz[u] = 1; |
| heavy_child[u] = 0; |
| |
| result_edges.push_back({p, u, w}); |
| |
| int curr = p; |
| int child_node = u; |
| |
| while (curr != 0) { |
| sz[curr]++; |
| if (heavy_child[curr] == 0) { |
| heavy_child[curr] = child_node; |
| } else { |
| |
| if (heavy_child[curr] != child_node) { |
| if (sz[child_node] > sz[heavy_child[curr]]) { |
| heavy_child[curr] = child_node; |
| } |
| } |
| } |
| child_node = curr; |
| curr = parent_node[curr]; |
| } |
| } |
|
|
| |
| int get_heavy_leaf(int u) { |
| while (heavy_child[u] != 0) { |
| u = heavy_child[u]; |
| } |
| return u; |
| } |
|
|
| void solve() { |
| int n; |
| if (!(cin >> n)) return; |
| |
| result_edges.clear(); |
| |
| for(int i=0; i<=n; ++i) { |
| children[i].clear(); |
| sz[i] = 1; |
| heavy_child[i] = 0; |
| parent_node[i] = 0; |
| } |
|
|
| if (n == 1) { |
| cout << "! " << endl; |
| return; |
| } |
|
|
| dist_root[1] = 0; |
| vector<pair<long long, int>> sorted_nodes; |
| sorted_nodes.reserve(n-1); |
| |
| |
| for (int i = 2; i <= n; ++i) { |
| dist_root[i] = query(1, i); |
| sorted_nodes.push_back({dist_root[i], i}); |
| } |
| |
| |
| sort(sorted_nodes.begin(), sorted_nodes.end()); |
| |
| |
| for (auto p : sorted_nodes) { |
| int u = p.second; |
| long long du = p.first; |
| |
| int curr = 1; |
| int next_target = -1; |
| long long next_dist = -1; |
| |
| while (true) { |
| int v; |
| long long d; |
| |
| |
| if (next_target == -1) { |
| v = get_heavy_leaf(curr); |
| |
| if (v == curr) { |
| add_node(curr, u, du - dist_root[curr]); |
| break; |
| } |
| d = query(u, v); |
| } else { |
| |
| v = next_target; |
| d = next_dist; |
| next_target = -1; |
| } |
| |
| |
| long long lca_depth = (du + dist_root[v] - d) / 2; |
| |
| |
| int w = v; |
| int child_towards_v = 0; |
| while (w != 0 && dist_root[w] > lca_depth) { |
| child_towards_v = w; |
| w = parent_node[w]; |
| } |
| |
| |
| if (w == v) { |
| add_node(v, u, du - dist_root[v]); |
| break; |
| } |
| |
| |
| vector<pair<int, int>> candidates; |
| for (int c : children[w]) { |
| if (c != child_towards_v) { |
| candidates.push_back({sz[c], c}); |
| } |
| } |
| |
| |
| if (candidates.empty()) { |
| add_node(w, u, du - dist_root[w]); |
| break; |
| } |
| |
| |
| sort(candidates.rbegin(), candidates.rend()); |
| |
| bool matched = false; |
| for (auto cand : candidates) { |
| int c = cand.second; |
| int leaf_c = get_heavy_leaf(c); |
| long long d2 = query(u, leaf_c); |
| long long lca2_depth = (du + dist_root[leaf_c] - d2) / 2; |
| |
| |
| if (lca2_depth > dist_root[w]) { |
| curr = c; |
| next_target = leaf_c; |
| next_dist = d2; |
| matched = true; |
| break; |
| } |
| } |
| |
| if (!matched) { |
| |
| add_node(w, u, du - dist_root[w]); |
| break; |
| } |
| } |
| } |
| |
| |
| cout << "!"; |
| for (const auto& e : result_edges) { |
| cout << " " << e.u << " " << e.v << " " << e.w; |
| } |
| cout << endl; |
| } |
|
|
| int main() { |
| ios_base::sync_with_stdio(false); |
| cin.tie(NULL); |
| int t; |
| if (cin >> t) { |
| while(t--) { |
| solve(); |
| } |
| } |
| return 0; |
| } |