0/1字典树

参考博客: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 
 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 }

 

你可能感兴趣的:(0/1字典树)