题目链接
题目大意:
从长度为 2n 序列中匹配 n 对数,并将匹配的两个数异或,使得的异或值最大值最小。
数据范围:n<2e5,每个数 < 2^30,都是正数。
题解
要使元素两两配对使得每对的异或和的最大值最小。先建01字典树
我们按位从高到低考虑,假设现在考虑第 i 位:
有偶数个 1 和偶数个 0:最小情况肯定是 0 对 0,1 对 1。于是分组递归下去下一位。
有奇数个 1 和奇数个 0:最小情况肯定是有一个 0 对 1 充当最大值,其他 0 对 0,1 对 1。
于是问题可以转换为:
有两组整数,各选择一个元素,求最小异或值。
const int maxn=2e5+7;
int tot=1,rt,a[32*maxn][2],n,m,cnt[32*maxn];
void insert(int x){
rt=1; cnt[1]++;
for(int i=30;i>=0;i--){
int k=(x>>i)&1;
if(!a[rt][k]) a[rt][k]=++tot;
rt=a[rt][k]; cnt[rt]++;
}
}
/*i:位数 l,r:左右子树 f:判断是否分组了*/
int dfs(int i,int l,int r,bool f){
//剪枝, !f&&!cnt[l]:未分组,且
if(i<0||(!f&&!cnt[l])) return 0;
//其左右子树都没有数,返回inf或(1<<(i+1))-1
if(!cnt[l]||!cnt[r]) return (1<<(i+1))-1;
//未分组
if(l==r){
//结点个数为奇数,留出一个1 ,开始分组
if(cnt[a[l][0]]&1) return dfs(i-1,a[l][0],a[l][1],1)+(1<<i);
//否则无贡献值,遍历其左右子树 继续不分组
return max(dfs(i-1,a[l][0],a[l][0],0),dfs(i-1,a[l][1],a[l][1],0));
}
//分组后
//Alice选左子树的点,左节点的点存在,Bob选 ——> 0对0
if(cnt[a[l][0]]&&cnt[a[r][0]])
return min(dfs(i-1,a[l][0],a[r][0],1),dfs(i-1,a[l][1],a[r][1],1));
//Alice选右子树的点,右节点的点存在,Bob选 ——> 1对1
if(cnt[a[l][1]]&&cnt[a[r][1]])
return min(dfs(i-1,a[l][0],a[r][0],1),dfs(i-1,a[l][1],a[r][1],1));
//无法0对0,1对1,再分组,加贡献 (1<<i)
return min(dfs(i-1,a[l][1],a[r][0],1),dfs(i-1,a[l][0],a[r][1],1))+(1<<i);
}
int main(){
n=read();
for(int i=0;i<2*n;i++){
int x=read();
insert(x);
}
int sum=dfs(30,1,1,0);
out(sum);
}
/*
样例一
2
0 1 3 5
输出一
4
样例二
10
974654030 99760550 750234695 255777344 907989127 917878091 818948631 690392797 579845317 549202360 511962375 203530861 491981716 64663831 561104719 541423175 301832976 252317904 471905694 350223945
输出二
268507123
*/