AcWing143. 最大异或对

异或

是一种逻辑运算,用于比较两个位(bit),当且仅当两个比较的位不同(一个为0,另一个为1)时,结果为1(真)。如果两个比较的位相同(都是0或都是1),结果为0(假)。在数学符号中,异或通常用符号“⊕”表示。

异或运算的规则如下:

• 0 ⊕ 0 = 0

• 0 ⊕ 1 = 1

• 1 ⊕ 0 = 1

• 1 ⊕ 1 = 0

直接暴力

6193de571fe74541ae526b87f669d7d0.png

显然会超时,这是需要考虑把这一步优化:

 987af16725c849f2911fd957d3b6fe5f.png

优化图解:

892ab909e5ab465fb7c455744ef833e9.png

 结论:

字典树不单单可以高效存储和查找字符串集合,还可以存储二进制数字

思路:

将每个数以二进制方式存入字典树,找的时候从最高位去找有无该位的异.

❶建树时,根据输入数字的对应的二进制串构造一个 Trie 树。

❷Trie 树的每个结点两个分支,分支指向的两个son结点分别表示当前位的数值为0或1

❸记录每次输入的数字转化成的二进制串,当前位为1,就走到数值为1的结点,否则走到0结点

❹这样每个数字对应的Trie中的路径就是唯一的。

因为要求异或对的值最大,可以用贪心的思想。

在第一个数字固定的情况下,尽可能地让第二个数的每一位都与第一个数的对应位相反,这样最终确定的第二个数与第一个数的异或值就最大,所以在查询时,遍历第一个串o(n),根据固定的第一个二进制串,每次尽可能走到与当前位的值相反的结点,这样的路径对应的就是与第一个二进制串异或值最大的二进制串,便利了这个数的位数次o(logn),所以总的时间复杂度o(n*logn);。

AC代码

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
//保存 Trie 树
int son[N * 31][2];  
int n, idx;

void insert(int x)
{
    int p = 0;//初始化指向根节点
    //从最高位开始,依次取出每一位
    for (int i = 31; i >= 0; i--)
    {   // 取出当前位
        int u = x >> i & 1;
         //如果树中不能走到当前数字
        //为当前数字创建新的节点,保存该数字
        if (!son[p][u])
            // 新节点编号为 idx + 1
            son[p][u] = ++idx; 
        p = son[p][u];
    }
}

int query(int x)
{
    //指向根节点
    int p = 0;
    // 保存与 x 异或结果最大的那个数
    int ret = 0;
     //从最高位开始,依次取出 x 的每一位
    for (int i = 31; i >= 0; i--)
    {
        // 取出 x 的当前位
        int u = x >> i & 1;
        //如果树中能走到 !u,就走到!u
        if (son[p][!u]){
            //走到!u
            p = son[p][!u];
            //更新 x 异或的对象
            ret = ret * 2 + !u;
        }  
        //没有!u,就只能走到u了
        else{
            p = son[p][u];
            //更新 x 异或的对象
            ret = ret * 2 + u; 
        }
    }
    //计算异或结果
    ret = ret ^ x;
    return ret;
}

int main()
{
    cin >> n;
    int maxXorNum= 0; 
    int x;
    for (int i = 0; i < n; i++)
    {
        cin >> x;
        insert(x);
        maxXorNum = max(maxXorNum, query(x));
    }

    cout << maxXorNum << endl;

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值