题意:
给出长度为 的两个序列 和 ,对序列内随意排序, ,这样得到序列 ,要求得出字典序最小的序列 。
其中 ,
分析:
要求字典序最小,那么只要贪心地从序列 中尽可能把异或值小的匹配全部选出来,然后从小到大排个序就是答案,
这个就可以很好地用01字典树实现了,先把序列 分别建成两棵01字典树,然后同时遍历,优先找同为0或同为1的(这样异或就可以尽可能地小),每次遍历完后删除这个值。
因为01字典树删除操作并不是真正的删除,而是利用一个记录访问次数结点次数的数组 ( 详见->传送门 ),所以只要遍历的时候令 即可。
以下代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1e6+50;
const int max_base=30;
int n;
struct bit_tire
{
int ch[30*maxn][2],val[30*maxn],num[30*maxn],tot;
void init()
{
tot=1;
ch[0][0]=ch[0][1]=0;
val[0]=0;
num[0]=0;
}
void ins(int x)
{
int u=0;
num[u]++;
for(int i=max_base;i>=0;i--)
{
int c=(x>>i)&1;
if(!ch[u][c])
{
ch[tot][0]=ch[tot][1]=0;
val[tot]=0;
num[tot]=0;
ch[u][c]=tot++;
}
u=ch[u][c];
num[u]++;
}
val[u]=x;
}
}t[2];
bool check(int ua,int ub,int c1,int c2) //子结点存在&&其num值>0
{
return t[0].ch[ua][c1]&&t[0].num[t[0].ch[ua][c1]]&&t[1].ch[ub][c2]&&t[1].num[t[1].ch[ub][c2]];
}
int get_min()
{
int ua=0,ub=0;
for(int i=max_base;i>=0;i--)
{
if(check(ua,ub,0,0)) //同为0
{
ua=t[0].ch[ua][0];
ub=t[1].ch[ub][0];
}
else if(check(ua,ub,1,1)) //同为1
{
ua=t[0].ch[ua][1];
ub=t[1].ch[ub][1];
}
else if(check(ua,ub,0,1)) //0和1
{
ua=t[0].ch[ua][0];
ub=t[1].ch[ub][1];
}
else //1和0
{
ua=t[0].ch[ua][1];
ub=t[1].ch[ub][0];
}
t[0].num[ua]--;
t[1].num[ub]--;
}
return t[0].val[ua]^t[1].val[ub];
}
int ans[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
t[0].init();
t[1].init();
scanf("%d",&n);
int a,b;
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
t[0].ins(a);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&b);
t[1].ins(b);
}
for(int i=1;i<=n;i++)
ans[i]=get_min();
sort(ans+1,ans+n+1);
for(int i=1;i<=n;i++)
{
if(i>1)
printf(" ");
printf("%d",ans[i]);
}
printf("\n");
}
return 0;
}