最大异或树

最大异或树

摘自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;
}
posted @ 2022-08-01 11:28  r涤生  阅读(29)  评论(0编辑  收藏  举报