可持久化trie树
SunsetLake · · 算法·理论
正常的 trie 树能解决一些字符串问题,
类似可持久化线段树,对于每个版本新建一个
- 新建当前字符串的根节点
p ,并记pre 表示上一个字符串的根节点。 - 若当前字符为
u ,那么给u 新开一个节点tr_{p,u}=++cnt 。 - 同时还要继承
pre 的其他字符,也就是对于一个字符ch \ne u ,tr_{p,ch}=tr_{pre,ch} ,相当于直接连过去。 - 接着让
p=tr_{p,u},pre=tr_{pre,u} ,向下建树,重复1,2,3 ,直到建完。
举个例子:若当前要插入这些字符串:
第一次: 第二次: 第三次: 第四次:
这样,一棵 trie 就建好了,从第
具体来说,我们需要引入一个
例如我们要访问区间
code
以
插入:
void insert(int pre,int p,int i,int k){//pre上一个数字的root,p当前数字的root,i是第几次插入的,k是这个数字要拆分的二进制位数
if(k<0){
last[p]=i;//建完了,返回
return;
}
int u=(num[i]>>k)&1;
if(pre)tr[p][u^1]=tr[pre][u^1];//继承上一个的字符
tr[p][u]=++cnt;
insert(tr[pre][u],tr[p][u],i,k-1);//递归新建节点
last[p]=max(last[tr[p][0]],last[tr[p][1]]);//更新last,从子树更新上来,也可以直接赋为i
}
查询(这里返回的是最大值的下标):
int query(int p,int x,int l,int k){//p当前节点,对于区间[l,r]赋为root[r],x是要查询的数,l左边界,来判断当前数是否在[l,r]中,k是二进制下第几位
if(k<0)return last[p];//last 相当于这个数的下标,因为我们是按前缀顺序插入的
int u=(x>>k)&1;
if(last[tr[p][u^1]]>=l)return query(tr[p][u^1],x,l,k-1);//在区间[l,r]中就优先走有贡献的位置
return query(tr[p][u],x,l,k-1);//否则不产生贡献,向下走
}
例题
P4735 最大异或和
板子。先记
注意:最好一开始 insert 一个
P5283 [十二省联考 2019] 异或粽子
还是用
再回头看题,前