最大异或树
最大异或树
摘自Acwing第143题:https://www.acwing.com/problem/content/145/
Trie树不仅可以用来高效存储读取字符串,还可以用来保存数字(二进制形式从高位开始存放)
思路
暴力怎么解决?
很简单就可以想到,使用两层for循环
for(枚举a[i])
{
for(枚举a[j])
{
res = max(res,a[i]^a[j]);
}
}
思考 枚举数对a[1]、a[5]与枚举数对a[5]、a[1]是一样的,我们可以设置内层for循环让他枚举 j < i这样可以有减少一部分重复
此时内层循环是O(n)的,总的时间复杂度是O(n^2)
使用trie树进一步优化
第二层for的目的是从a[i],a[0]~a[i-1]中找到最大异或对
我们可以把所有数存到trie树中(二进制形式存放)
当第一层for枚举到a[i]的时候先插入到trie树中再查找与之相匹配的最大异或对,可以发现从trie树的根节点开始,找到叶子结点就结束。这个时候的时间复杂度是O(31)的(数字大小范围是0~2^31)
可以得到很大优化
代码
M = 31 * N
整数个数不超过 1e5,每个整数占据 31 个结点位置,不同整数结点有交集,因此需要的结点个数(即最终的 idx) 小于 31e5
#include<iostream>
#include<algorithm>
using namespace std ;
const int N = 100010 ,M = 31 * N;
int n,a[N];
int son[M][2],idx;
void insert(int x)
{
int p = 0;
for(int i = 30; i >= 0; i --) // 从高位到低位插入到trie树中
{
int u = x >> i & 1;
if(!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
}
}
int search(int x)
{
int p = 0;
int res = 0;
for(int i = 30; i >= 0; i --)
{
int u = x >> i & 1;
if(son[p][!u])
{
res = res * 2 + 1;
p = son[p][!u];
}
else
{
res = res * 2 + 0;
p = son[p][u];
}
}
return res;
}
int main()
{
scanf("%d",&n);
int res = 0;
for(int i = 0; i < n; i ++)
{
scanf("%d",&a[i]);
insert(a[i]);
res = max(res,search(a[i]));
}
printf("%d",res);
return 0;
}
rds_blogs