原题地址:http://acm.hdu.edu.cn/showproblem.php?pid=6625
题意:给出一个 a a a数组和 b b b数组, a a a数组和 b b b数组一一异或得到 c c c数组,允许打乱 a a a数组和 b b b数组的顺序,使得得到的 c c c数组的字典序最小。
思路:我们可以对 a a a数组和 b b b数组分别建立两颗字典树。然后我们同步扫这两个字典树,如果分别到达了某一层的节点,如果在这个节点下都有往 0 0 0走的路,将两棵树同步向 0 0 0方向扫过去,如果都有往 1 1 1走的路,就将两棵树同步往 1 1 1走,(异或上相同的数字得到 0 0 0肯定是最优的),反之对应的位置只能一个为 0 0 0,一个为 1 1 1。然后按照这个策略同时走到底为进行一遍,得到一个解。然后我们只需要进行 n n n遍,就可以得到 n n n个解。
注意:
1.得到一个解的同时要删除得到这个解路径上的点,也就是删除对应的
a
a
a数组中的一个值和
b
b
b数组中的一个值。
2.最后的答案需要进行排序。因为虽然按照策略有
00
00
00,
11
11
11就匹配,但是还会存在
01
,
10
01,10
01,10的情况,你无法预料到这两种情况哪个的字典序小,所以最后的答案是需要排序的。
#include <bits/stdc++.h>
#define eps 1e-8
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define CLR(x, y) memset((x),y,sizeof(x))
#define fuck(x) cerr << #x << "=" << x << endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int seed = 131;
const int maxn = 32 * 1e5 + 5;
const int mod = 1e9 + 7;
int T, n;
int a[maxn], b[maxn];
vector<int> ans;
int pos[2], trie[2][maxn][2], num[2][maxn], val[2][maxn];
/*
pos是节点标号,num[root]=x,表示到root节点的数字是x
val[root]=x,表示到节点root是几个数字的前缀
*/
void Insert(int id, int x) {//插入数字x,高位开始
int root = 0;
for (int i = 31; i >= 0; i--) {
int t = (x >> i) & 1;
if (!trie[id][root][t]) {
trie[id][root][t] = ++pos[id];
}
root = trie[id][root][t];
val[id][root]++;
}
num[id][root] = x;
}
bool check(int id, int root, int x) {
return trie[id][root][x] && val[id][trie[id][root][x]];
}
int dfs() {
int root0 = 0;
int root1 = 0;
for (int i = 31; i >= 0; i--) {
if (check(0, root0, 0) && check(1, root1, 0)) {
root0 = trie[0][root0][0];
root1 = trie[1][root1][0];
val[0][root0]--;
val[1][root1]--;
} else if (check(0, root0, 1) && check(1, root1, 1)) {
root0 = trie[0][root0][1];
root1 = trie[1][root1][1];
val[0][root0]--;
val[1][root1]--;
} else if (check(0, root0, 0) && check(1, root1, 1)) {
root0 = trie[0][root0][0];
root1 = trie[1][root1][1];
val[0][root0]--;
val[1][root1]--;
} else if (check(0, root0, 1) && check(1, root1, 0)) {
root0 = trie[0][root0][1];
root1 = trie[1][root1][0];
val[0][root0]--;
val[1][root1]--;
}
}
return num[0][root0] ^ num[1][root1];
}
void Delete(int id, int x) {//删除数字x
int root = 0;
for (int i = 31; i >= 0; i--) {
int t = (x >> i) & 1;
root = trie[id][root][t];
val[id][root]--;
}
}
int main() {
scanf("%d", &T);
while (T--) {
pos[0] = 0;
pos[1] = 0;
ans.clear();
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
Insert(0, a[i]);
}
for (int i = 1; i <= n; i++) {
scanf("%d", &b[i]);
Insert(1, b[i]);
}
for (int i = 1; i <= n; i++) {
int x = dfs();
ans.push_back(x);
}
sort(ans.begin(), ans.end());
int sz = ans.size();
for (int i = 0; i < sz - 1; i++) {
printf("%d ", ans[i]);
}
printf("%d\n", ans[sz - 1]);
for (int j = 0; j < 2; j++) {
for (int i = 0; i < pos[j]; i++) {
num[j][i]=0;
trie[j][i][0]=0;
trie[j][i][1]=0;
val[j][i]=0;
}
}
}
return 0;
}