| #include <bits/stdc++.h> |
| using namespace std; |
|
|
| using ll = long long; |
| using ull = unsigned long long; |
|
|
| |
| static unordered_map<ull, ll> distCache; |
|
|
| |
| static unordered_set<ull> edgeSet; |
| static vector<tuple<int,int,ll>> edges; |
|
|
| static int n; |
|
|
| |
| inline ull packKey(int u, int v) { |
| if (u > v) swap(u, v); |
| return ( (ull)u << 32 ) | (ull)v; |
| } |
|
|
| ll getDist(int u, int v) { |
| if (u == v) return 0; |
| ull key = packKey(u, v); |
| auto it = distCache.find(key); |
| if (it != distCache.end()) return it->second; |
| cout << "? " << u << " " << v << endl; |
| cout.flush(); |
| ll ans; |
| if (!(cin >> ans)) { |
| |
| exit(0); |
| } |
| distCache.emplace(key, ans); |
| return ans; |
| } |
|
|
| inline void setCachedDist(int u, int v, ll d) { |
| if (u == v) return; |
| ull key = packKey(u, v); |
| distCache.emplace(key, d); |
| } |
|
|
| inline void addEdge(int u, int v, ll w) { |
| if (u == v) return; |
| ull key = packKey(u, v); |
| if (edgeSet.find(key) == edgeSet.end()) { |
| edgeSet.insert(key); |
| edges.emplace_back(u, v, w); |
| } |
| } |
|
|
| void solveGroup(const vector<int>& S, int a) { |
| if (S.size() <= 1) return; |
|
|
| |
| ll maxd = -1; |
| int b = a; |
| vector<ll> da(S.size()); |
| for (size_t i = 0; i < S.size(); ++i) { |
| ll d = getDist(a, S[i]); |
| da[i] = d; |
| if (d > maxd) { |
| maxd = d; |
| b = S[i]; |
| } |
| } |
| if ((int)S.size() == 2) { |
| |
| int u = S[0], v = S[1]; |
| ll w = getDist(u, v); |
| addEdge(u, v, w); |
| return; |
| } |
|
|
| |
| vector<ll> db(S.size()); |
| for (size_t i = 0; i < S.size(); ++i) { |
| db[i] = getDist(b, S[i]); |
| } |
|
|
| ll D = getDist(a, b); |
|
|
| |
| vector<pair<ll,int>> pathNodes; |
| pathNodes.reserve(S.size()); |
|
|
| |
| unordered_map<ll,int> pos2node; |
| pos2node.reserve(S.size() * 2); |
|
|
| |
| unordered_map<int, vector<int>> groups; |
|
|
| for (size_t i = 0; i < S.size(); ++i) { |
| ll z2 = da[i] + db[i] - D; |
| |
| ll z = z2 / 2; |
| if (z == 0) { |
| ll pos = da[i]; |
| pathNodes.emplace_back(pos, S[i]); |
| } |
| } |
|
|
| sort(pathNodes.begin(), pathNodes.end()); |
| pos2node.reserve(pathNodes.size()*2 + 1); |
| for (auto &p : pathNodes) { |
| pos2node[p.first] = p.second; |
| } |
|
|
| |
| for (size_t i = 1; i < pathNodes.size(); ++i) { |
| int u = pathNodes[i-1].second; |
| int v = pathNodes[i].second; |
| ll w = pathNodes[i].first - pathNodes[i-1].first; |
| addEdge(u, v, w); |
| } |
|
|
| |
| for (size_t i = 0; i < S.size(); ++i) { |
| ll z2 = da[i] + db[i] - D; |
| ll z = z2 / 2; |
| if (z > 0) { |
| ll pos = da[i] - z; |
| auto it = pos2node.find(pos); |
| if (it == pos2node.end()) { |
| |
| continue; |
| } |
| int proj = it->second; |
| groups[proj].push_back(S[i]); |
| |
| setCachedDist(proj, S[i], z); |
| } |
| } |
|
|
| |
| for (auto &kv : groups) { |
| int proj = kv.first; |
| vector<int> sub; |
| sub.reserve(kv.second.size() + 1); |
| sub.push_back(proj); |
| for (int v : kv.second) sub.push_back(v); |
| solveGroup(sub, proj); |
| } |
| } |
|
|
| int main() { |
| ios::sync_with_stdio(false); |
| cin.tie(nullptr); |
|
|
| int T; |
| if (!(cin >> T)) return 0; |
| while (T--) { |
| if (!(cin >> n)) return 0; |
|
|
| distCache.clear(); |
| distCache.reserve(1 << 20); |
| edgeSet.clear(); |
| edgeSet.reserve((size_t)max(1, n - 1) * 2); |
| edges.clear(); |
| edges.reserve(max(1, n - 1)); |
|
|
| if (n <= 1) { |
| cout << "!" << endl; |
| cout.flush(); |
| continue; |
| } |
|
|
| int s = 1; |
| |
| vector<int> allNodes(n); |
| for (int i = 0; i < n; ++i) allNodes[i] = i + 1; |
| for (int i = 2; i <= n; ++i) { |
| getDist(s, i); |
| } |
|
|
| |
| solveGroup(allNodes, s); |
|
|
| |
| cout << "!"; |
| for (auto &e : edges) { |
| int u, v; ll w; |
| tie(u, v, w) = e; |
| cout << " " << u << " " << v << " " << w; |
| } |
| cout << endl; |
| cout.flush(); |
| } |
| return 0; |
| } |