File size: 5,992 Bytes
1fd0050
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#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; // upper triangular (i<j) stored row-wise
    vector<long long> offset; // offset for i: starting index in tri for pairs (i, j>i)
    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);
        // offset[i] = sum_{k=1}^{i-1} (n - k) = (i-1)*n - (i-1)*i/2
        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) { // i<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;
    // find diameter endpoints within S using two-sweep
    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);

    // collect nodes on path from a to 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);
    });

    // add edges along the path
    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);
    }

    // prepare mapping: node -> index in path, and distances from a along path
    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]);
    }

    // groups for recursion: for each path index, collect nodes not on path that project to that node
    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; // 2*delta
        long long delta = delta2 / 2;
        long long xdist = da - delta; // distance from a to projection on path
        // binary search xdist in distAPath
        auto it = lower_bound(distAPath.begin(), distAPath.end(), xdist);
        if (it == distAPath.end() || *it != xdist) {
            // theoretically should not happen in a valid tree metric
            // but if it does, we skip to avoid crash
            continue;
        }
        int pos = (int)(it - distAPath.begin());
        groups[pos].push_back(v);
    }

    // recurse on each group
    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 by some chance we have more than n-1 due to duplicates, we can trim by using a map
        if ((int)edges.size() != n - 1) {
            // Deduplicate just in case
            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;
}