参考博客:https://blog.csdn.net/weixin_43847416/article/details/95048031
https://blog.csdn.net/qq_41292370/article/details/90680145
01字典树主要用于解决求异或最值的问题。01字典树的插入和查找操作跟字典树基本上差不多,只是字典树插入的是一个字符串,而01字典树插入的是一个二进制的数字串
现在来说说为什么01字典树可以解决异或最值问题:假如现在给出一个数,现在我们要找一个数与该数异或值最大,我们应该怎么找,这里我们要用的(0^1=1,0^0=0,1^1=0)基本异或操作,现在我们只需要把这个数化成二进制数,然后从高位依次向下找(为什么从高位开始找,后面再解释),如果这个数该位是0,我们就要找该位是1的数,要是该位是1的话,我们就要找该位是0的数。例如:我们要找二进制为1001的数的异或最大值,另一个数肯定是0110,这样异或下来为1111,结果最大。
- 01字典树就是一棵最多 32层(如果插入的数在int型之内)的二叉树,其每个节点的两条边分别表示二进制的某一位的值为 0 还是为 1. 将某个路径上边的值连起来就得到一个二进制串。
- 节点个数为 1 的层(最高层)节点的边对应着二进制串的最高位。
- 可通过贪心的策略来寻找与 x异或结果最大的数,即优先找和 x二进制的未处理的最高位值不同的边对应的点,这样保证结果最大。
模板:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=2e6+5; 5 6 int n,m; 7 int tris[maxn][2]; 8 ll val[maxn]; //节点值 9 ll num[maxn]; //记录每个节点被访问的次数 10 int tot; 11 12 void _insert(ll x){ //插入d 13 int root=0; 14 for(int i=32;i>=0;i--){ 15 int id=(x>>i)&1; //获得二进制位的值 16 if( !trie[root][id] ) trie[root][id]=++tot; 17 root=trie[root][id]; 18 num[root]++; //记录该节点被访问几次 19 } 20 val[root]=d; //记录值 21 } 22 23 ll find_(ll x){ //查询所有数中和x异或结果最大的数 24 int root=0; 25 for(int i>=32;i>=0;i--){ 26 int id=(x>>i)&1; 27 if( trie[root][id^1] ) root=trie[root][id^1]; //利用贪心策略,优先寻找和当前位不同的数 28 else root=trie[root][id]; 29 } 30 return val[root]; 31 }