题意
给定两个长度为1e5的数组a、b,在对a、b数组分别重新排序后,求相同长度的数组c,使得c[i]=a[i] xor b[i],使得数组 c 的字典序最小。
思路
分别对a、b建字典树,能走\(11/00\)就走,否则就走\(01/10\),找出最小的n个数,从小到大排序之后即为所求数组。
Code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
const int maxm = maxn*31;
int tree[2][maxm][2];
int num[2][maxm];
int tot[2];
void insert(int x, int st) {
int sta[32], top = 0;
while(x) sta[++top] = x%2, x/=2;
while(top<30) sta[++top] = 0;
int ptr = 0;
for (int x, i=top; i; --i) {
x = sta[i];
if(!tree[st][ptr][x]) tree[st][ptr][x] = ++tot[st];
++num[st][ptr];
ptr = tree[st][ptr][x];
}
++num[st][ptr];
}
int query() {
int res = 0, ptr0 = 0, ptr1 = 0;
for (int i=0; i<30; ++i) {
res <<= 1;
if(tree[0][ptr0][1] && tree[1][ptr1][1] && num[0][tree[0][ptr0][1]] && num[1][tree[1][ptr1][1]]) {
ptr0 = tree[0][ptr0][1];
ptr1 = tree[1][ptr1][1];
--num[0][ptr0];
--num[1][ptr1];
} else if(tree[0][ptr0][0] && tree[1][ptr1][0] && num[0][tree[0][ptr0][0]] && num[1][tree[1][ptr1][0]]) {
ptr0 = tree[0][ptr0][0];
ptr1 = tree[1][ptr1][0];
--num[0][ptr0];
--num[1][ptr1];
} else if(tree[0][ptr0][1] && tree[1][ptr1][0] && num[0][tree[0][ptr0][1]] && num[1][tree[1][ptr1][0]]) {
ptr0 = tree[0][ptr0][1];
ptr1 = tree[1][ptr1][0];
--num[0][ptr0];
--num[1][ptr1];
res |= 1;
} else if(tree[0][ptr0][0] && tree[1][ptr1][1] && num[0][tree[0][ptr0][0]] && num[1][tree[1][ptr1][1]]) {
ptr0 = tree[0][ptr0][0];
ptr1 = tree[1][ptr1][1];
--num[0][ptr0];
--num[1][ptr1];
res |= 1;
}
}
return res;
}
int main() {
int T, n;
scanf("%d", &T);
while(T--) {
tot[0] = tot[1] = 0;
scanf("%d", &n);
for (int i=0; i<=n*31; ++i) {
tree[0][i][0] = tree[0][i][1] = tree[1][i][0] = tree[1][i][1] = 0;
num[0][i] = num[1][i] = 0;
}
for (int x, i=1; i<=n; ++i) scanf("%d", &x), insert(x, 0);
for (int x, i=1; i<=n; ++i) scanf("%d", &x), insert(x, 1);
vector<int> ans;
for (int i=1; i<=n; ++i) ans.push_back(query());
sort(ans.begin(), ans.end());
for (int i=0; i<n; ++i) printf("%d ", ans[i]);
puts("");
}
return 0;
}