AcWing 143. (Trie树+贪心)最大异或对
原题链接
简单
作者:
Avetre
,
2025-04-10 21:16:42
· 湖北
,
所有人可见
,
阅读 3
分析
1.暴力做法(TLE)
:每次枚举一对数取异或并更新最大值,两重循环
,外层循环取一对数中右边这个数,内层循环取左边这个数(左边数的索引比右边数的索引小),如此确保不会重复取一对数
2.Trie树优化
:使用Trie树优化内层循环(O(n)⇒O(31)),总时间复杂度O(n2)⇒O(31∗n),具体实现方式如下
(1)将每个数
转化为31个二进制0/1
由最高位到最低位
存储在Trie树中,每次insert操作
插入一个数后,使用query操作
查询并返回当前Trie树中与插入的数的最大异或值结果,更新全局最大异或值
(2)判读最大异或值
时,采用贪心策略
,从最高位到最低位
依次逐位判断(位数越高,占异或结果的权重越大)当前Trie树中是否存在与该位相反的节点可走
,若有则选择走相反位,若没有,则只能走相同位,更新当前查询的最大异或值并递归下一位
,最后返回当前Trie树中与插入的数的最大异或结果
注意:
1.采用先插入后查询
,虽然会多一次自身与自身异或(结果为0)的判断,但不影响最大值结果,且能省去Tries树为空时查询前需要执行的判断操作
2.son[N][M]
两个维度的确定:
(1)N
:Trie树中最大节点个数
,即要存储的元素个数*每个元素用几个节点存储
(2)M
:每个节点最多有几个子节点
,即每个节点的种类数
(一般为字符/数字种类数)
3.此题能建立Trie树的关键在于考虑每个数的前导0
代码:
#include<bits/stdc++.h>
using namespace std;
const int n = 1e5 + 10,m = 31 * n;
int a[n];
int son[m][2];
int idx;
int N;
int res;
void insert(int x)
{
int p = 0;
for(int i = 30;i >= 0;i --)
{
int u = x >> i & 1;
if(!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
}
}
int query(int x)
{
int p = 0;
int res = 0;
for(int i = 30;i >= 0;i --)
{
int u = x >> i & 1;
if(son[p][!u])
{
p = son[p][!u];
res = (res << 1) + !u;
}
else
{
p = son[p][u];
res = (res << 1) + u;
}
}
return res;
}
int main()
{
cin >> N;
for(int i = 1;i <= N;i ++)
{
cin >> a[i];
insert(a[i]);
res = max(res,a[i] ^ query(a[i]));
}
cout << res << endl;
return 0;
}