最大异或对
在给定的 N个整数 A1,A2……AN中选出两个进行 xor
(异或)运算,得到的结果最大是多少?
输入格式
第一行输入一个整数 N。
第二行输入 N个整数 A1~AN。
输出格式
输出一个整数表示答案。
本题采用字典树
首先,我们将所有读入的要进行比较的数存入字典树中。并利用son[N][2]数组记录每个节点的位置,这种记录方式是记录孩子的位置,我们可以很容易发现每个节点除了叶子节点外最多有两个分支,即为二进制表示的0或者1,由数据范围我们很容易得知我们记录的每一个数转为二进制存入trie后长度最多为31,所以开son数组的N=31100010,以son[1][1]=3为例,该数组元素表示1号节点的一个孩子值为1,并且这个孩子的节点为3。
存入一个数:我们从根节点开始遍历,最多31层。所以我们每个数据都是按二进制31位存,而先导0并不影响异或的结果。从第30位开始,利用u=x>>i&1得到第i位的u是0或是1,如果son[p][u]为0则说明能继续存,son[p][u]=++idx;,p再指向其孩子son[p][u]。
查询一个数的最大异或值:
同样从高位往低位查,越高位数据产生异或1说明该异或值越大,正印证了我们将高位存在距离根节点较近的层,我们去查这棵搭建好的trie树的子节点有没有可以异或为1的值,即son[p][!u]存不存在,如果存在就用res记录这个值,即该位为1,res=res2+1,例如binary(10)2+1就为b(100+1)=b(101),b代表二进制,然后以p=son[p][!u]往下一层遍历。
如果son[p][!u]==0,那么该位异或为0,即res=res2,p=son[p][u]。
代码如下(示例):
#include<iostream>
using namespace std;
const int N=31*100010;
const int m=100010;
int son[N][2];
int idx;
int a[m];
void insert(int x)
{
int p=0;
for(int i=30;i>=0;i--)
{
int u=(x>>i)&1;
if(son[p][u]==0)
{
idx++;
son[p][u]=idx;
}
p=son[p][u];
}
}
int query(int x)
{
int p=0;
int res=0;
for(int i=30;i>=0;i--)
{
int u=(x>>i)&1;
if(son[p][!u])
{
p=son[p][!u];
res=res*2+1;//异或后整体乘2来左移一位,例如高位10下一位异或后为1则binary(10)*2+1变为100+1为101
}
else
{
p=son[p][u];
res=res*2;
}
}
return res;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
insert(a[i]);
}
int max=0;
for(int i=0;i<n;i++)
{
if(query(a[i])>=max)
{
max=query(a[i]);
}
}
cout<<max;
return 0;
}
总结
该题考查了字典树(trie)的用法,通过将每一个数据的二进制值存入字典树中,每次查询异或最大值只需遍历31层即可,避免了O(n^2)的复杂度,降为O(n)。