The XOR Largest Pair[01-Trie树 经典模板题]

The XOR Largest Pair(01-Trie 经典题)

一、题目描述

在给定的 N 个整数 A1, A2, ..., AN 中选出两个进行异或运算,问结果最大是多少?

对应题目:信息学奥赛一本通 472 【例题2】The XOR Largest Pair

二、思路分析

暴力做法

枚举所有二元组 (i, j),计算 Ai ^ Aj,取最大值。时间复杂度 O(N²),N ≤ 10⁵ 时明显超时。

01-Trie 做法

把每个整数看作一个 31 位的二进制串(因为 Ai < 2^31),从高位到低位插入一棵 Trie 树。

Trie 中每条从根到叶子的路径,就对应一个整数的二进制表示。

查询某个数 x 的最大异或值时:

  • 从高位到低位逐位考虑
  • 每一位都贪心地走与当前位相反的子树(因为异或相同得 0,不同得 1)
  • 如果相反位不存在,只能走相同位

这样每一步都能让结果的当前位尽量为 1,最终得到的就是与 x 异或最大的数。

为什么贪心是对的

异或结果的二进制表示中,高位权重大于所有低位权重之和。
从最高位开始决策(因为 2^b > 2^b-1,高位一旦定下来,低位再怎么选都翻不了盘)。所以要 for (int b = MAXB-1; b >= 0; --b)。
所以从高位到低位贪心,每一位都尽量取 1,全局一定最优。

三、算法流程

  1. 预处理一个空的 Trie,根节点编号为 0
  2. 把每个 Ai 按二进制从高位(第 30 位)到低位(第 0 位)插入 Trie
  3. 对每个 Ai,在 Trie 中查询能与它异或得到的最大值
  4. 所有查询结果取最大值,即为答案

四、AC 代码

#include<bits/stdc++.h>
using namespace std;

const int MAXB = 31;

struct Node
{
    int ch[2];     // ch[i] 表示下一个位置是 i 的下标
    Node()
    {
        ch[0] = ch[1] = -1;
    }
};

vector<Node> trie;

int createNode()
{
    trie.emplace_back(Node());
    return trie.size() - 1;
}

void insert(int t)
{
    int u = 0;
    for (int i = MAXB - 1; i >= 0; i--)
    {
        int a = (t >> i) & 1;
        if (trie[u].ch[a] == -1) trie[u].ch[a] = createNode();
        u = trie[u].ch[a];
    }
}

int query(int x) //查询
{
    int u = 0, res = 0;
    for (int i = MAXB - 1; i >= 0; i--)
    {
        int a = (x >> i) & 1;
        if (trie[u].ch[1 - a] != -1)
        {
            res |= 1 << i;
            u = trie[u].ch[1 - a];
        }
        else u = trie[u].ch[a];
    }
    return res;
}

int main()
{
    int n;
    cin >> n;

    trie.reserve((size_t)n * MAXB + 1);  // 预留空间,避免反复扩容
    createNode();

    int arr[n];
    for (int i = 0; i < n; i++)
    {
        cin >> arr[i];
        insert(arr[i]);
    }
    int res = 0;
    for (int i = 0; i < n; i++)
        res = max(res, query(arr[i]));

    cout << res << endl;
    return 0;
}

五、代码说明

1. Trie 的存储结构

struct Node
{
    int ch[2];  // 只有两个子节点:0 和 1
    Node() { ch[0] = ch[1] = -1; }
};
  • ch[0] 表示当前位为 0 时的子节点
  • ch[1] 表示当前位为 1 时的子节点
  • -1 表示该子节点不存在

2. 插入

从第 30 位到第 0 位依次取出二进制位,沿 Trie 向下建立路径。

3. 查询

if (trie[u].ch[1 - a] != -1)
{
    res |= 1 << i;         // 当前位异或结果为 1
    u = trie[u].ch[1 - a]; // 走相反位
}
else u = trie[u].ch[a];    // 只能走相同位
  • 优先走相反位,让当前位异或结果为 1
  • 相反位不存在时走相同位,当前位结果为 0

六、复杂度

  • o(n * log(n) )

七、总结

要点 内容
数据结构 01-Trie(每个节点只有 0/1 两个儿子)
核心思想 从高位到低位贪心走相反位
时间复杂度 O(N × 31)
关键细节 位数从 30 开始,最高位优先
posted @ 2026-09-24 14:58  leaderling  阅读(5)  评论(0)    收藏  举报