#include #include #include #include #include #include #include #include using namespace std; struct Trie { static constexpr int MAXB = numeric_limits::digits - 1; Trie(): nxt{ nullptr, nullptr } {} ~Trie() { for (int j = 0; j < 2; ++j) { if (nxt[j]) { delete nxt[j]; } } } array nxt; }; void insert(Trie* t, uint64_t x) { for (int j = Trie::MAXB; j >= 0; --j) { bool b = (x & (1ULL << j)) > 0; if (!t->nxt[b]) { t->nxt[b] = new Trie; } t = t->nxt[b]; } } map solve(Trie* t, int b = Trie::MAXB) { if (t->nxt[0] == nullptr) { // {0 => 0} in nicer languages // note that it's impossible to have an empty left child and a populated right child assert(b == -1); return {{0, 0}}; } else if (t->nxt[1] == nullptr) { return solve(t->nxt[0], b - 1); } else { auto m1 = solve(t->nxt[0], b - 1); auto m2 = solve(t->nxt[1], b - 1); uint64_t bit = (1ULL << b); for (auto p : m2) { // k is with the bit, k' is without uint64_t k = (p.first ^ bit); uint64_t kp = p.first; // do the swap m1[k] = m1[kp]; m1[kp] = (p.second ^ bit); } return m1; } } int main() { int n; cin >> n; Trie* root = new Trie(); vector a(n); for (int i = 0; i < n; ++i) { cin >> a[i]; insert(root, a[i]); } /* Solution strategy: build a trie. Solve each subtree independently, then merge the results When solving a subtree, ignore the high order bits you've passed through on the way. To merge, consider each value a_i in the right subtree. It maps to some b_i. Let a'_i equal a_i without the bit at this level of the tree. Because of the input guarantees, it appears in the left subtree. Swap the values b_i and b'_i. This is guaranteed to not cause any colliisons. Overall runtime is O(n log n log |max A|). There are log |max A| levels in the trie, and the map adds an extra log factor. It's probably possible to omit the map, but ¯\_(ツ)_/¯. */ auto b = solve(root); delete root; for (int i = 0; i < n; ++i) { cout << b[a[i]] << '\n'; } }