简介
字典树,就是将字典树中的字符 ~ 换为二进制的 ,得以存储数的二进制形式。
对于结点 ,有代表下一位为 的左儿子: ,代表下一位为 的右儿子:
而字典树的根( )表示最高位 位,故其代表0;字典树的叶结点 则表示最低位,且其具有一个 ,即从根( )遍历到该叶子结点 所表示的数(非叶子结点的 )。
用途:
将数字以二进制形式存储为字典树,可以快速地按二进制前缀进行查询,故 可以处理数与数之间进行二进制运算的问题。
其中较为典型的就是异或最值问题。
- HYSBZ - 4260 Codechef REBXOR —— 序列区间连续异或和最值
- POJ - 3764 The xor-longest Path —— 边权树连续异或和最值
- HDU - 5536 Chip Factory —— 带删除操作01字典树求异或最值
- HDU - 6625 three arrays —— 两序列匹配异或最值
模板:
①不带删除操作
a. 初始化
字典树数组一般开的比较大,全部置零会比较耗时,故一般如下操作
const int max_base=31; //int类型为例
int ch[31*maxn][2],val[31*maxn],tot; //数组大小为max_base*maxn
void init()
{
tot=1; //根节点编号为0
ch[0][0]=ch[0][1]=0; //暂无左右儿子
val[0]=0; //值为0
}
b. 插入值
void ins(int x)
{
int u=0;
//从高位到低位对x进行二进制拆分
for(int i=max_base;i>=0;i--)
{
int c=(x>>i)&1;
if(!ch[u][c]) //该子结点尚不存在,则创建
{
//对新的子结点进行初始化
ch[tot][0]=ch[tot][1]=0;
val[tot]=0;
ch[u][c]=tot++;
}
u=ch[u][c]; //下一层
}
val[u]=x; //到达叶子结点,赋值
}
c. 查询与 异或的最值
异或运算:
所以,找最小值,就要找与 当前位相同的;找最大值,就要找与 当前位相异的。
int query_max(int x) //query_min(int x)
{
int u=0;
for(int i=max_base;i>=0;i--)
{
int c=(x>>i)&1;
if(ch[u][c^1]) //if(ch[u][c])
u=ch[u][c^1]; // u=ch[u][c]
else //else
u=ch[u][c]; // u=ch[u][c^1]
}
return x^val[u];
}
②带删除操作
实际上此处删除操作并不是真正的从字典树上删除这个数,而是再定义一个数组 ,表示结点 被访问的次数(即表示点 是多少个数的前缀)。
- 在插入或复原操作中,令路径上的 ;
- 在删除操作中,则令路径上的 ;
- 而在查询遍历时,则不仅要判断子结点 是否存在,还要判断其 是否
a. 初始化
int ch[31*maxn][2],val[31*maxn],num[31*maxn],tot;
void init()
{
tot=1;
ch[0][0]=ch[0][1]=0;
val[0]=0;
num[0]=0; //num初始化为0
}
b. 插入值
void ins(int x)
{
int u=0;
num[u]++;
for(int i=max_base;i>=0;i--)
{
int c=(x>>i)&1;
if(!ch[u][c])
{
ch[tot][0]=ch[tot][1]=0;
val[tot]=0;
num[tot]=0; //新结点num初始化为0
ch[u][c]=tot++;
}
u=ch[u][c];
num[u]++; //路径上的num+1
}
val[u]=x;
}
c. 删除/还原值x
因为不是真正地删除,所以同样可以还原 之前被删除 的 。
void updata(int x,int op) //op=-1,删除
{ //op=1,还原
int u=0;
num[u]+=op;
for(int i=max_base;i>=0;i--)
{
int c=(x>>i)&1;
u=ch[u][c];
num[u]+=op; //遍历路径
}
}
d. 查询与 异或的最值
int query_max(int x) //query_min(int x)
{
int u=0;
for(int i=max_base;i>=0;i--)
{
int c=(x>>i)&1;
if(ch[u][c^1]&&num[ch[u][c^1]]) //if(ch[u][c]&&num[ch[u][c]])
u=ch[u][c^1]; // u=ch[u][c]
else //else
u=ch[u][c]; // u=ch[u][c^1]
}
return x^val[u];
}