File size: 5,610 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 199 200 201 202 | #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// Function to perform an interactive query
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]; // 0 if none
vector<int> children[MAXN];
struct Edge {
int u, v;
long long w;
};
vector<Edge> result_edges;
// Function to add a new node to the current tree
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;
// Update sizes and maintain heavy children pointers up to the root
while (curr != 0) {
sz[curr]++;
if (heavy_child[curr] == 0) {
heavy_child[curr] = child_node;
} else {
// Update heavy child if the modified subtree becomes larger
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];
}
}
// Get the leaf of the heavy path starting from u
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();
// Reset data structures for the new test case
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);
// Step 1: Query distances from root (node 1) to all other nodes
for (int i = 2; i <= n; ++i) {
dist_root[i] = query(1, i);
sorted_nodes.push_back({dist_root[i], i});
}
// Step 2: Sort nodes by distance from root to process in increasing order of depth
sort(sorted_nodes.begin(), sorted_nodes.end());
// Step 3: Incrementally build the tree
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;
// Determine target node to query in the current subtree
if (next_target == -1) {
v = get_heavy_leaf(curr);
// If curr is a leaf, it must be the parent
if (v == curr) {
add_node(curr, u, du - dist_root[curr]);
break;
}
d = query(u, v);
} else {
// Reuse query result if available from previous iteration
v = next_target;
d = next_dist;
next_target = -1;
}
// Calculate depth of LCA(u, v)
long long lca_depth = (du + dist_root[v] - d) / 2;
// Find the node w on path curr...v that corresponds to this depth
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 LCA is v, then v is the parent
if (w == v) {
add_node(v, u, du - dist_root[v]);
break;
}
// Collect light children of w (excluding the heavy path we just checked)
vector<pair<int, int>> candidates;
for (int c : children[w]) {
if (c != child_towards_v) {
candidates.push_back({sz[c], c});
}
}
// If no light children, w is the parent
if (candidates.empty()) {
add_node(w, u, du - dist_root[w]);
break;
}
// Sort light children by size (heuristic)
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 LCA descends into c's subtree
if (lca2_depth > dist_root[w]) {
curr = c;
next_target = leaf_c;
next_dist = d2;
matched = true;
break;
}
}
if (!matched) {
// u is not in any of the light children's subtrees
add_node(w, u, du - dist_root[w]);
break;
}
}
}
// Output the reconstructed edges
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;
} |