| #include <bits/stdc++.h> |
| using namespace std; |
|
|
| struct FastScanner { |
| static inline int gc() { |
| return getchar(); |
| } |
| bool readLongLong(long long &out) { |
| int c = gc(); |
| while (c != EOF && (c != '-' && (c < '0' || c > '9'))) c = gc(); |
| if (c == EOF) return false; |
| int sign = 1; |
| if (c == '-') { sign = -1; c = gc(); } |
| long long x = 0; |
| while (c >= '0' && c <= '9') { |
| x = x * 10 + (c - '0'); |
| c = gc(); |
| } |
| out = x * sign; |
| return true; |
| } |
| bool readInt(int &out) { |
| long long tmp; |
| if (!readLongLong(tmp)) return false; |
| out = (int)tmp; |
| return true; |
| } |
| }; |
|
|
| struct DistMatrix { |
| int n; |
| vector<long long> tri; |
| vector<long long> offset; |
| DistMatrix() : n(0) {} |
| DistMatrix(int _n) { init(_n); } |
| void init(int _n) { |
| n = _n; |
| long long m = 1LL * n * (n - 1) / 2; |
| tri.assign((size_t)m, 0); |
| offset.assign(n + 2, 0); |
| |
| for (int i = 1; i <= n; ++i) { |
| offset[i] = 1LL * (i - 1) * n - 1LL * (i - 1) * i / 2; |
| } |
| } |
| inline long long &atPairIdx(int i, int j) { |
| return tri[(size_t)(offset[i] + (j - i - 1))]; |
| } |
| inline long long get(int i, int j) const { |
| if (i == j) return 0; |
| if (i < j) { |
| return tri[(size_t)(offset[i] + (j - i - 1))]; |
| } else { |
| return tri[(size_t)(offset[j] + (i - j - 1))]; |
| } |
| } |
| }; |
|
|
| int N; |
| DistMatrix D; |
| vector<tuple<int,int,long long>> edges; |
|
|
| void reconstruct(const vector<int> &S) { |
| if (S.size() <= 1) return; |
| |
| int s0 = S[0]; |
| int t = s0; |
| long long best = -1; |
| for (int x : S) { |
| long long dv = D.get(s0, x); |
| if (dv > best) { best = dv; t = x; } |
| } |
| int u = t; |
| best = -1; |
| for (int x : S) { |
| long long dv = D.get(t, x); |
| if (dv > best) { best = dv; u = x; } |
| } |
| int a = u, b = t; |
| long long Sab = D.get(a, b); |
|
|
| |
| vector<int> pathNodes; |
| pathNodes.reserve(S.size()); |
| for (int x : S) { |
| if (D.get(a, x) + D.get(x, b) == Sab) pathNodes.push_back(x); |
| } |
| sort(pathNodes.begin(), pathNodes.end(), [&](int x, int y){ |
| return D.get(a, x) < D.get(a, y); |
| }); |
|
|
| |
| for (size_t i = 0; i + 1 < pathNodes.size(); ++i) { |
| int u1 = pathNodes[i], v1 = pathNodes[i+1]; |
| long long w = D.get(a, v1) - D.get(a, u1); |
| edges.emplace_back(u1, v1, w); |
| } |
|
|
| |
| vector<int> idxInPath(N + 1, -1); |
| vector<long long> distAPath(pathNodes.size()); |
| for (size_t i = 0; i < pathNodes.size(); ++i) { |
| idxInPath[pathNodes[i]] = (int)i; |
| distAPath[i] = D.get(a, pathNodes[i]); |
| } |
|
|
| |
| vector<vector<int>> groups(pathNodes.size()); |
| vector<char> inPath(N + 1, 0); |
| for (int p : pathNodes) inPath[p] = 1; |
|
|
| for (int v : S) if (!inPath[v]) { |
| long long da = D.get(a, v), db = D.get(b, v); |
| long long delta2 = da + db - Sab; |
| long long delta = delta2 / 2; |
| long long xdist = da - delta; |
| |
| auto it = lower_bound(distAPath.begin(), distAPath.end(), xdist); |
| if (it == distAPath.end() || *it != xdist) { |
| |
| |
| continue; |
| } |
| int pos = (int)(it - distAPath.begin()); |
| groups[pos].push_back(v); |
| } |
|
|
| |
| for (size_t i = 0; i < groups.size(); ++i) { |
| if (!groups[i].empty()) { |
| vector<int> subS; |
| subS.reserve(groups[i].size() + 1); |
| subS.push_back(pathNodes[i]); |
| for (int v : groups[i]) subS.push_back(v); |
| reconstruct(subS); |
| } |
| } |
| } |
|
|
| int main() { |
| ios::sync_with_stdio(false); |
| cin.tie(nullptr); |
| FastScanner fs; |
| int T; |
| { |
| int tmp; |
| if (!fs.readInt(tmp)) return 0; |
| T = tmp; |
| } |
| for (int tc = 0; tc < T; ++tc) { |
| int n; |
| { |
| int tmp; |
| fs.readInt(tmp); |
| n = tmp; |
| } |
| N = n; |
| D.init(n); |
| long long m = 1LL * n * (n - 1) / 2; |
| for (int i = 1; i <= n - 1; ++i) { |
| for (int j = i + 1; j <= n; ++j) { |
| long long val; |
| fs.readLongLong(val); |
| D.atPairIdx(i, j) = val; |
| } |
| } |
| edges.clear(); |
| edges.reserve((size_t)max(0, n - 1)); |
| vector<int> all(n); |
| for (int i = 0; i < n; ++i) all[i] = i + 1; |
| reconstruct(all); |
|
|
| cout << "!"; |
| |
| if ((int)edges.size() != n - 1) { |
| |
| map<pair<int,int>, long long> mp; |
| for (auto &e : edges) { |
| int u, v; long long w; |
| tie(u, v, w) = e; |
| if (u > v) swap(u, v); |
| mp[{u, v}] = w; |
| } |
| edges.clear(); |
| edges.reserve(mp.size()); |
| for (auto &kv : mp) { |
| edges.emplace_back(kv.first.first, kv.first.second, kv.second); |
| } |
| } |
| for (auto &e : edges) { |
| int u, v; long long w; |
| tie(u, v, w) = e; |
| cout << " " << u << " " << v << " " << w; |
| } |
| cout << "\n"; |
| } |
| return 0; |
| } |