01字典树解析与模板

本文将解答:
1.01字典树是什么
2.01字典树用途
3.01字典树实现代码
4.每个数按位插入顺序

一、01字典树是什么
首先我们都知道普通字典树(不懂?忘记了?点这里 ),字典树是插入字符串,利用字符串的公共前缀来减少查找时间,01字典树其实就是按位插入数字(将数字转化为2进制串),所谓“按位”即将该数字按2进制拆分为每一位是0或1的数字

二、01字典树的用处
01字典树一般都是用于求异或最值问题。

三、01字典树的实现
和普通字典树并没有区别,只是插入的是二进制01串,只需要两个数组就行,一个是tree[][]数组,一个是val[]数组,val数组是建树插入x时记录该条树枝的最后一个节点为x ,记录后就可直接取出,不用再进行转化,方便异或。

代码实现放在最后

四、每个数按位插入顺序为什么是从高位开始的
01字典树树要求的是异或最值,我们都知道要利用字典树进行贪心策略

怎么贪心?
首先我们知道异或是:两个位上的数字相同置0,不同为1
假如我们求的是最大异或值,我们在每一位的选取上当然是尽量要选与当前位不同的。
先不管选取先后顺序,假如我们现在选到第i位,我们不管选0还是1,都能使最终答案数变化 2i,2i 我们就称它为贡献值i越大贡献值2i就越大,我们改变这个数的能力就越大,故我们从高位开始插入和查询

c++代码实现(有详细注释)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define max_size 100005 
int num=0;
int tree[32*max_size][2];
//int book[32*max_size];01字典树不需要,因为长度是一样的,即层数都相同 
//int sum[32*max_size];这个是求前缀数的,由于插入的每个数的最后一层的层数相同 ,所以也不用 
int val[32*max_size];//01字典树特有 建树插入x时记录该条树枝的最后一个节点为x 
void insert(int x)//建树
{
	int now=0;
	//为什么是逆序呢 ? 
	//要异或最大-贪心:要选不同的 (求异或值最小就选相同的), 因为第i位的贡献是2^i,
	//要使异或值最大,i就要从高位开始选 
	for(int i=31;i>=0;i--){
		int id=(x>>i)&1;
		if(!tree[now][id])
		tree[now][id]=++num;
		now=tree[now][id];
		//sum[now]++;
	}
	//book[now]=1;//标记是否为一个数的结束位 
	val[now]=x;//到这个节点为止才是一个数x 
	return;
}
int search(int x)
{
	int now=0;
	for(int i=31;i>=0;i--){
		int id=(x>>i)&1;
		////贪心策略:要求异或最大值,要优先选择和当前位不同的数 
		if(tree[now][id^1])//与当前位不同的数存在 
		now=tree[now][id^1];
		else//不存在 
		now=tree[now][id];
	}
	return val[now];
}
int main(void)
{
	int t,n,m,x;
	scanf("%d",&t);
	int i=0;
	while(t--)
	{
		scanf("%d %d",&n,&m);
		while(n--)
		{
			scanf("%d",&x);
			insert(x);
		}
		scanf("%d",&x);m--;
		printf("Case #%d:\n",++i); 
		printf("%d\n",search(x));
		while(m--)
		{
			scanf("%d",&x);
			printf("%d\n",search(x));
		}
		//重置字典树
		memset(tree,0,sizeof(tree));
		num=0; 
	}
	return 0;
}
发布了68 篇原创文章 · 获赞 15 · 访问量 8997

猜你喜欢

转载自blog.csdn.net/qq_43791377/article/details/104643751