File size: 4,851 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 | #include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Edge {
int u, v, w;
};
int n;
vector<long long> depth;
vector<vector<int>> adj;
vector<int> sub_sz;
vector<int> parent;
vector<Edge> result_edges;
// Helper to query distance between two nodes
long long query_dist(int u, int v) {
cout << "? " << u << " " << v << endl;
long long d;
cin >> d;
return d;
}
// Update subtree sizes starting from u upwards to the root
void update_sz(int u) {
int curr = u;
while (curr != 0) {
sub_sz[curr]++;
curr = parent[curr];
}
}
// Find the parent of node u in the current tree.
// We search within the subtree rooted at subtree_root.
// Returns the parent node index, or -1 if u is not in the subtree of subtree_root.
int find_parent(int u, int subtree_root) {
int curr = subtree_root;
// Construct the heavy path starting from curr
int leaf = curr;
int temp = curr;
while (!adj[temp].empty()) {
int heavy_child = -1;
int max_sz = -1;
for (int v : adj[temp]) {
if (sub_sz[v] > max_sz) {
max_sz = sub_sz[v];
heavy_child = v;
}
}
if (heavy_child != -1) {
temp = heavy_child;
} else {
break;
}
}
leaf = temp;
// Query distance from u to the leaf of the heavy path
long long d = query_dist(u, leaf);
// Calculate the weighted depth of the LCA of u and leaf
// formula: dist(u, leaf) = depth[u] + depth[leaf] - 2 * depth[lca]
long long lca_depth_val = (depth[u] + depth[leaf] - d) / 2;
// Identify the LCA node. It must be on the path from leaf to root.
// We walk up from leaf until we find the node with the correct depth.
int lca = leaf;
while (lca != 0 && depth[lca] > lca_depth_val) {
lca = parent[lca];
}
// If the identified LCA is strictly above curr, then u branches off
// before entering the subtree of curr. Thus u is not in this subtree.
if (depth[lca] < depth[curr]) {
return -1;
}
// If lca is exactly the leaf, then the leaf is the parent of u
if (lca == leaf) return leaf;
// Identify the child of lca that lies on the heavy path towards leaf
int child_on_path = -1;
int walker = leaf;
while (walker != lca) {
child_on_path = walker;
walker = parent[walker];
}
// We know u is in the subtree of lca, but NOT in the subtree of child_on_path
// (otherwise the LCA would have been lower).
// So u must be attached to lca directly or be in the subtree of one of the light children.
vector<pair<int, int>> candidates;
for (int child : adj[lca]) {
if (child != child_on_path) {
candidates.push_back({sub_sz[child], child});
}
}
// If there are no other children, u must be attached to lca
if (candidates.empty()) return lca;
// Sort light children by size descending to optimize search
sort(candidates.rbegin(), candidates.rend());
for (auto& cand : candidates) {
int res = find_parent(u, cand.second);
if (res != -1) {
return res;
}
}
// If not found in any light child's subtree, the parent is lca
return lca;
}
void solve() {
cin >> n;
if (n == 1) {
cout << "! " << endl;
return;
}
// Reset data structures
depth.assign(n + 1, 0);
adj.assign(n + 1, vector<int>());
sub_sz.assign(n + 1, 0);
parent.assign(n + 1, 0);
result_edges.clear();
// Determine depth of all nodes relative to node 1
vector<pair<long long, int>> nodes;
for (int i = 2; i <= n; ++i) {
depth[i] = query_dist(1, i);
nodes.push_back({depth[i], i});
}
// Sort nodes by depth so we build the tree top-down
sort(nodes.begin(), nodes.end());
// Initialize tree with root 1
sub_sz[1] = 1;
parent[1] = 0;
// Insert nodes one by one
for (auto& p : nodes) {
int u = p.second;
// Find parent of u in the current tree
int p_u = find_parent(u, 1);
// Update tree structure
parent[u] = p_u;
adj[p_u].push_back(u);
sub_sz[u] = 1;
update_sz(p_u);
// Record edge
result_edges.push_back({p_u, u, (int)(depth[u] - depth[p_u])});
}
// Output result
cout << "!";
for (const auto& e : result_edges) {
cout << " " << e.u << " " << e.v << " " << e.w;
}
cout << endl;
}
int main() {
// Optimize I/O operations
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int t;
if (cin >> t) {
while (t--) {
solve();
}
}
return 0;
} |