題意
給定一個由 \(n\) 個非負整陣列成的陣列 \(a\)。
如果 \(a_i \oplus a_j < 4\),那麼你就可以交換 \(a_i、a_j\),其中,\(\oplus\) 是按位異或。
求出操作若干次後,字典序最小的序列。
資料範圍:\(1 \le n \le 2 \times 10^5\),\(0 \le a_i \le 10^9\)。
題解
性質:$ a_i \oplus a_j < 4 $ 的充分必要條件是:如果不考慮 \(a_i、a_j\) 二進位制下的最低兩位,那麼\(a_i、a_j\) 相等。
我們可以將 \(a_1 \sim a_n\) 劃分為若干個集合,每個集合內部實現升序排序,然後放回對應的位置。由於值域很大,因此這個操作可以用 std::map
實現。
時間複雜度為 \(\mathcal{O}(nlogn)\)。
點選檢視程式碼
#include <bits/stdc++.h>
using i64 = int64_t;
inline int read() {
bool sym = false; int res = 0; char ch = getchar();
while (ch < '0' or ch > '9') sym |= (ch == '-'), ch = getchar();
while (ch >= '0' and ch <= '9') res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return sym ? -res : res;
}
void solve() {
int n = read();
std::vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
a[i] = read();
}
std::map<int, std::priority_queue<int>> f;
for (int i = 1; i <= n; i++) {
f[a[i] ^ (a[i] & 3)].push(-a[i]);
}
for (int i = 1; i <= n; i++) {
printf("%d%s", -f[a[i] ^ (a[i] & 3)].top(), i == n ? "\n" : " ");
f[a[i] ^ (a[i] & 3)].pop();
}
}
int main() {
int T = read();
while (T--) {
solve();
}
return 0;
}