01字典树的模板+例题


以前听学长讲的字典树,但是除了当时刷的字典树的题后就没怎么碰到字典树的题目了。前两天刷题的时候看别人博客看到了01字典树,便好奇的学习了一下,总的来说01字典树在解决异或问题是十分的方便。
通常是给你一个数组,问你一段连续的异或和最大是多少,正常思路贪心dp啥的都会一头雾水,但是用01字典树就能很快的解决。

01字典树主要用于解决求异或最值的问题。

原理

01字典树和普通的字典树原理类似,只不过把插入字符改成了插入二进制串的每一位(0或1)。

01字典树是按位插入和查询的。因为如果一个数,它的高位值较大,那么这个数的值较大。所以我们插入和查询时是从最高位开始进行的。

  • 01字典树是一棵最多 32层的二叉树,其每个节点的两条边分别表示二进制的某一位的值为 0 还是为 1. 将某个路径上边的值连起来就得到一个二进制串。
  • 节点个数为 1 的层(最高层)节点的边对应着二进制串的最高位。
  • 节点值 val为 0时表示到当前节点为止不能形成一个数,否则 val[i]=数值。辅助数组val来记录原数值。
  • 可通过贪心的策略来寻找与 x异或结果最大的数,即优先找和 x二进制的未处理的最高位值不同的边对应的点,这样保证结果最大。

模板:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int MAXN=100005
int n,m;
int trie[32*MAXN][2];
ll val[32*MAXN];//结点值
ll num[32*MAXN];//每个结点被访问的次数
int tot;

void insert(ll d)//往01字典树里插入d
{
    int root=0;
    for(int i=32;i>=0;i--)//最多32层
    {
        int id=(d>>i)&1;//获得这一个bit位的值
        if(!trie[root][id]) trie[root][id]=++tot;
        root=trie[root][id];
        num[root]++;
    }
    val[root]=d;
}

void update(ll x,ll add)//更新插入或删除x后每个结点被访问的次数
{
  int root=0;
  for(int i=32;i>=0;i--)
  {
    int id=(x>>i)&1;
    root=trie[root][id];
    num[root]+=add;
  }
}

ll query(ll d)//查询所有数中和d异或结果最大的数
{
    int root=0;
    for(int i=32;i>=0;i--)
    {
        int id=(d>>i)&1;
        //利用贪心策略,优先寻找和当前位不同的数
        if(trie[root][id^1]) root=trie[root][id^1];
        else root=trie[root][id];
    }
    return val[root];
}


例题


HDU 5536
传送门 HDU 5536
题解


HDU 4825
传送门 HDU 4825
01字典树模板题
题解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值