The XOR Largest Pair(字典树+贪心)

本文介绍了一种使用01字典树解决寻找整数数组中两数最大异或值的问题。通过构建01字典树并遍历每个数,利用异或特性找到能产生最大异或结果的配对。

题目描述

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

输入

第一行一个整数 NN。

第二行 NN 个整数 AiAi 。

输出

一个整数表示答案。

输入样例

5

2 9 4 7 0

输出样例

14

数据范围

1<=N<=105,0<=Ai<231

思路

此题不看数据范围的话肯定会想到暴力,但是数据范围就写在那里,怎么办呢?

其实看到异或的话,我们首先想到的是二进制,根据异或的性质,如果我们对于每一位的数字都能找到一个相反的

位的话,就肯定能找到一个最大值,比如(000 和111),这样的话,我们可以构造一颗01字典树,然后对于每个数,根据异或的规则,对于每一位都在这棵树里面找到相反位,如果找到相反位,那么他们异或的值就可以变大,如果找不到,就不变

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=4e6+5;
int tree[maxn][2];
int tot;
int insert(int x)
{
    int  root=0;
    for(int i=31;i>=0;i--)
    {
        int c=(x>>i)&1;
        if(!tree[root][c])tree[root][c]=++tot;
        root=tree[root][c];
    }
}
int search(int x)
{
    int root=0;
    int ans=1;
    for(int i=31;i>=0;i++)
    {
        int c=(x>>i)&1;
        int o=c^1;
        if(tree[root][o])ans=(ans<<1)|1,root=tree[root][o];
        else ans=ans<<1,root=tree[root][c];
        
    }
    return ans;
}
int main()
{
    int n;
    cin>>n;
    int i;
    int tmp,ans1=0;;
    for(i=0;i<n;i++)
    {
        cin>>tmp;
        ans1=max(ans1,searth(tmp));
        insert(tmp);
    }
    cout<<ans1<<endl;
    return 0;
}


<think>我们已知:1.定义dx[i]为根节点(假设为0)到节点i的路径上所有边权的异或值。通过一次DFS可以求出所有dx[i]。2.树上任意两点u和v之间的路径异或值等于dx[u]xordx[v](因为从根到u和根到v的路径中,重叠的部分会相互抵消,剩下u到v的路径)。3.问题转化为:在dx数组(有n个数)中,找到两个数,使它们的异或值最大。使用01-Trie树来解决最大异或对问题:-将每个数的二进制表示(从高位到低位)插入到Trie树中(固定位数,比如31位,因为w<2^31,所以最多31位)。-然后对于每个数,在Trie树中查询与它异或结果最大的数:从根开始,每次尽量走与当前位相反的位(这样异或后当前位为1),如果没有相反的位则走相同的位。步骤:1.建树:使用邻接表存储树结构。2.DFS遍历树,计算每个节点的dx值(从根节点0开始,根节点的dx值为0)。3.构建01-Trie:将所有dx值插入Trie树。4.对每个dx值,在Trie树中查询最大异或值,然后取所有结果的最大值。注意:由于树是无根的,我们任意选择一个根(比如0号节点)即可。代码实现细节:-Trie树节点:每个节点有两个孩子,分别表示0和1。-插入:从高位到低位,根据每一位的值(0或1)建立路径。-查询:从高位到低位,尽量选择与当前位不同的分支(如果存在),这样异或后该位为1。时间复杂度:O(n*31)≈O(n),可以接受。注意:多组测试数据,需要初始化。下面给出C++代码实现:</think>### 带权树上最长异或路径的C++实现(使用Trie优化) #### 算法核心思想 1. **路径异或转换** 定义$d[u]$为节点$u$到根节点的路径异或和,则任意两点$u,v$间路径的异或和为: $$ \text{XOR}(u,v) = d[u] \oplus d[v] $$ 这个性质成立是因为路径重叠部分会相互抵消[^2]。 2. **问题转化** 原问题等价于: $$ \max_{u,v \in V} \left( d[u] \oplus d[v] \right) $$ 3. **01-Trie优化** 使用二进制字典树高效查询最大异或对: - 将每个$d[i]$的二进制表示插入Trie - 对每个$d[i]$,在Trie中查询能产生最大异或值的路径 #### C++代码实现 ```cpp #include <iostream> #include <vector> #include <algorithm> #include <cstring> using namespace std; const int MAXN = 100010; const int BIT_LEN = 31; // 31位整数处理 struct Edge { int v, w; Edge(int _v, int _w) : v(_v), w(_w) {} }; vector<Edge> graph[MAXN]; int d[MAXN]; // 存储节点到根节点的异或和 // 01字典树节点 struct TrieNode { TrieNode* child[2]; TrieNode() { child[0] = child[1] = nullptr; } }; // DFS计算前缀异或和 void dfs(int u, int parent, int cur_xor) { d[u] = cur_xor; for (const Edge& e : graph[u]) { if (e.v == parent) continue; dfs(e.v, u, cur_xor ^ e.w); } } // 插入数字到字典树 void insertTrie(TrieNode* root, int num) { TrieNode* node = root; for (int i = BIT_LEN; i >= 0; i--) { int bit = (num >> i) & 1; if (!node->child[bit]) { node->child[bit] = new TrieNode(); } node = node->child[bit]; } } // 查询与num异或的最大值 int queryTrie(TrieNode* root, int num) { TrieNode* node = root; int max_xor = 0; for (int i = BIT_LEN; i >= 0; i--) { int bit = (num >> i) & 1; int toggled = 1 - bit; // 期望的相反位 if (node->child[toggled]) { max_xor |= (1 << i); node = node->child[toggled]; } else { node = node->child[bit]; } } return max_xor; } int main() { int n; while (cin >> n) { // 初始化 for (int i = 0; i < n; i++) graph[i].clear(); memset(d, 0, sizeof(d)); // 读入树结构 for (int i = 0; i < n-1; i++) { int u, v, w; cin >> u >> v >> w; graph[u].push_back(Edge(v, w)); graph[v].push_back(Edge(u, w)); } // DFS计算前缀异或和 dfs(0, -1, 0); // 构建字典树 TrieNode* root = new TrieNode(); for (int i = 0; i < n; i++) { insertTrie(root, d[i]); } // 查询最大异或值 int max_xor = 0; for (int i = 0; i < n; i++) { max_xor = max(max_xor, queryTrie(root, d[i])); } cout << max_xor << endl; // 释放字典树内存(实际竞赛可省略) // ... 遍历删除Trie节点 ... } return 0; } ``` #### 关键组件解析 1. **数据结构** - `Edge`:存储邻接点和边权 - `TrieNode`:字典树节点,包含0/1两个分支 - `d[]`:存储节点到根节点的异或和 2. **核心函数** - `dfs()`:深度优先遍历计算路径异或和 - `insertTrie()`:将数字插入01字典树 - `queryTrie()`:在字典树中查询最大异或值 3. **算法流程** ```mermaid graph TD A[输入树结构] --> B[DFS计算d数组] B --> C[构建01字典树] C --> D[遍历d数组查询最大异或] D --> E[输出结果] ``` #### 复杂度分析 - **时间复杂度**:$O(n \log W)$ DFS遍历$O(n)$,字典树操作$O(\log W)$,其中$W$为最大边权 - **空间复杂度**:$O(n \log W)$ 字典树存储所有二进制表示 #### 测试用例 ```plaintext 输入: 4 0 1 3 1 2 5 1 3 6 计算过程: d[0]=0(0000), d[1]=3(0011) d[2]=3^5=6(0110), d[3]=3^6=5(0101) 最大异或: d[0]^d[2]=0^6=6(0110) → 最大值 输出:6 ``` > 该实现基于路径异或性质[^2]和Trie树优化[^1],可高效处理$n \leq 10^5$的数据规模。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值