| #include <bits/stdc++.h> |
| using namespace std; |
|
|
| struct Solver { |
| int n; |
| vector<tuple<int,int,long long>> edges; |
| unordered_map<unsigned long long, long long> cache; |
|
|
| static unsigned long long keyPair(int a, int b) { |
| if (a > b) swap(a, b); |
| return ( (unsigned long long)a << 32 ) | (unsigned long long)b; |
| } |
|
|
| long long query(int u, int v) { |
| if (u == v) return 0; |
| unsigned long long key = keyPair(u, v); |
| auto it = cache.find(key); |
| if (it != cache.end()) return it->second; |
| cout << "? " << u << " " << v << endl; |
| cout.flush(); |
| long long d; |
| if (!(cin >> d)) { |
| |
| exit(0); |
| } |
| cache.emplace(key, d); |
| return d; |
| } |
|
|
| void add_edge(int u, int v, long long w) { |
| |
| edges.emplace_back(u, v, w); |
| } |
|
|
| void buildGroup(vector<int>& nodes, vector<long long>& distRoot, int r) { |
| int m = (int)nodes.size(); |
| if (m <= 1) return; |
|
|
| |
| int idx_x = 0; |
| long long Dmax = distRoot[0]; |
| for (int i = 1; i < m; ++i) { |
| if (distRoot[i] > Dmax) { |
| Dmax = distRoot[i]; |
| idx_x = i; |
| } |
| } |
| int x = nodes[idx_x]; |
| long long D = Dmax; |
|
|
| |
| vector<long long> dx(m); |
| for (int i = 0; i < m; ++i) { |
| dx[i] = query(x, nodes[i]); |
| } |
|
|
| |
| vector<pair<long long,int>> pathList; |
| vector<int> pathIndices; |
| pathList.reserve(m); |
| pathIndices.reserve(m); |
| for (int i = 0; i < m; ++i) { |
| if (distRoot[i] + dx[i] == D) { |
| pathIndices.push_back(i); |
| } |
| } |
|
|
| |
| sort(pathIndices.begin(), pathIndices.end(), [&](int a, int b){ |
| return distRoot[a] < distRoot[b]; |
| }); |
|
|
| |
| for (int i = 0; i + 1 < (int)pathIndices.size(); ++i) { |
| int u = nodes[pathIndices[i]]; |
| int v = nodes[pathIndices[i+1]]; |
| long long w = distRoot[pathIndices[i+1]] - distRoot[pathIndices[i]]; |
| add_edge(u, v, w); |
| } |
|
|
| |
| int k = (int)pathIndices.size(); |
| vector<long long> pathDist(k); |
| vector<int> pathNode(k); |
| for (int i = 0; i < k; ++i) { |
| int idx = pathIndices[i]; |
| pathDist[i] = distRoot[idx]; |
| pathNode[i] = nodes[idx]; |
| } |
|
|
| |
| vector<vector<int>> groupIdx(k); |
| for (int i = 0; i < m; ++i) { |
| long long numerator = distRoot[i] + D - dx[i]; |
| |
| long long t = numerator / 2; |
| |
| int pos = int(lower_bound(pathDist.begin(), pathDist.end(), t) - pathDist.begin()); |
| if (pos == k || pathDist[pos] != t) { |
| |
| if (pos >= k) pos = k - 1; |
| else if (pos < 0) pos = 0; |
| } |
| groupIdx[pos].push_back(i); |
| } |
|
|
| |
| for (int pos = 0; pos < k; ++pos) { |
| vector<int>& idxs = groupIdx[pos]; |
| if ((int)idxs.size() <= 1) continue; |
| int g = pathNode[pos]; |
|
|
| vector<int> subNodes; |
| subNodes.reserve(idxs.size()); |
| vector<long long> subDist; |
| subDist.reserve(idxs.size()); |
| for (int idx : idxs) { |
| subNodes.push_back(nodes[idx]); |
| long long dd = (distRoot[idx] + dx[idx] - D) / 2; |
| subDist.push_back(dd); |
| } |
| buildGroup(subNodes, subDist, g); |
| } |
| } |
|
|
| void solve_case() { |
| cin >> n; |
| edges.clear(); |
| cache.clear(); |
|
|
| if (n == 1) { |
| cout << "! " << endl; |
| cout.flush(); |
| return; |
| } |
|
|
| |
| int r = 1; |
| vector<int> nodes(n); |
| vector<long long> distRoot(n); |
| for (int i = 0; i < n; ++i) nodes[i] = i + 1; |
| distRoot[0] = 0; |
| for (int i = 2; i <= n; ++i) { |
| distRoot[i-1] = query(r, i); |
| } |
|
|
| buildGroup(nodes, distRoot, r); |
|
|
| |
| cout << "! "; |
| for (auto &e : edges) { |
| int u, v; |
| long long w; |
| tie(u, v, w) = e; |
| cout << u << " " << v << " " << w << " "; |
| } |
| cout << endl; |
| cout.flush(); |
| } |
| }; |
|
|
| int main() { |
| ios::sync_with_stdio(false); |
| cin.tie(nullptr); |
|
|
| int T; |
| if (!(cin >> T)) return 0; |
| Solver solver; |
| while (T--) { |
| solver.solve_case(); |
| } |
| return 0; |
| } |