[您有新的未分配科技点]可,可,可持久化!?------0-1Trie和可持久化Trie普及版讲解

这一次,我们来了解普通Trie树的变种:0-1Trie以及在其基础上产生的可持久化Trie(其实,普通的Trie也可以可持久化,只是不太常见)

先简单介绍一下0-1Trie:一个0-1Trie节点只有两个子节点,分别代表0和1;从根节点开始,第一层代表限制的最高位,依次往下直到最底层,代表二进制第0位。

0-1Trie上的一条链所表示的数字,就是Trie树中的一个数字。0-1Trie除了节点和插入方式与普通的Trie树略有不同之外,其他操作都是和Trie树完全一样的。在维护这个节点插入过的size之后,0-1Trie甚至可以做一些平衡树的题……

下面给2道比较简单的例题:

bzoj3489 异或之 http://www.lydsy.com/JudgeOnline/problem.php?id=3489

bzoj3224 普通平衡树 http://www.lydsy.com/JudgeOnline/problem.php?id=3224

值得注意的是,0-1Trie无法处理负权值,因此,我们可以给每个数加上一个大的修正值delta,使得所有值都成为非负的。最后我们在减去delta即可。

下面给出0-1Trie版的普通平衡树代码,很短,但是的确可以AC:

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 typedef long long LL;
 5 const int inf=0x7fffffff,delta=10000100;
 6 LL bin[50];
 7 struct Trie
 8 {
 9     Trie *ch[2];int size;
10     Trie(){size=0;ch[1]=ch[0]=NULL;}
11 }*null=new Trie(),*root;
12 inline Trie* newTrie(){Trie *o=new Trie();o->ch[0]=o->ch[1]=null;return o;}
13 inline void insert(int x)
14 {
15     Trie *rt=root;
16     for(int i=30;~i;i--)
17     {
18         int d=(x&bin[i])>>i;
19         if(rt->ch[d]==null)rt->ch[d]=newTrie();
20         rt=rt->ch[d],rt->size++;
21     }
22 }
23 inline void del(int x)
24 {
25     Trie *rt=root;
26     for(int i=30;~i;i--)
27         rt=rt->ch[(x&bin[i])>>i],rt->size--;
28 }
29 inline int getrank(int x)
30 {
31     Trie *rt=root;int ret=0;
32     for(int i=30;~i;i--)
33     {
34         if((x&bin[i])>>i)ret+=rt->ch[0]->size;
35         rt=rt->ch[(x&bin[i])>>i];
36     }
37     return ret;
38 }
39 inline int getval(int rank)
40 {
41     Trie *rt=root;int ret=0;
42     for(int i=30;~i;i--)
43     {
44         if(rt->ch[0]->size>=rank)rt=rt->ch[0];
45         else rank-=rt->ch[0]->size,ret|=bin[i],rt=rt->ch[1];
46     }
47     return ret;
48 }
49 int main()
50 {
51     bin[0]=1;for(int i=1;i<=40;i++)bin[i]=bin[i-1]<<1;
52     root=newTrie();null->ch[0]=null->ch[1]=null;
53     int m,opt,x;scanf("%d",&m);
54     while(m--)
55     {
56         scanf("%d%d",&opt,&x);
57         switch(opt)
58         {
59             case 1:insert(x+delta);break;
60             case 2:del(x+delta);break;
61             case 3:printf("%d\n",getrank(x+delta)+1);break;
62             case 4:printf("%d\n",getval(x)-delta);break;
63             case 5:printf("%d\n",getval(getrank(x+delta))-delta);break;
64             case 6:printf("%d\n",getval(getrank(x+delta+1)+1)-delta);break;
65         }
66     }
67 }

接下来,我们在0-1Trie的基础上,介绍可持久化Trie。

可持久化Trie树和前面两种可持久化数据结构一样,也是通过复制节点来实现可持久化操作。

在插入的时候,我们也是复制路径上的节点,由于可持久化Trie和主席树一样具有区间可减性,所以我们直接像主席树那样区间相减即可。

具体代码,长得和之前的可持久化Treap差不多……下面给出插入的代码(可能比较丑……)

1 //bin[i]数组为预处理的2的i次方
2 void insert(Trie *&o,Trie *old,int val,int i)
3 {
4     if(i<0)return;
5     int d=((val&bin[i])==bin[i]);//判断当前为是0还是1
6     o->ch[d]=newTrie();o->ch[d^1]=old->ch[d^1];
7     o->ch[d]->size=old->ch[d]->size+1;
8     insert(o->ch[d],old->ch[d],val,i-1);
9 }

可持久化Trie树经常用来处理与异或有关的k小问题。一般来说,我们都是把0-1Trie可持久化来维护数字运算,很少有把字符串的Trie可持久化的题目。

这里再给出两道可持久化Trie的基础题:

bzoj4103[Thu Summer Camp 2015]异或运算 http://www.lydsy.com/JudgeOnline/problem.php?id=4103

bzoj3166[Heoi2013]Alo http://www.lydsy.com/JudgeOnline/problem.php?id=3166

我的题解:http://www.cnblogs.com/LadyLex/p/7281860.html

可持久化Trie是一种和主席树同样优秀的数据结构,无疑是一种新的解题思路。希望大家能从我的博客中有所收获:)

时间: 2024-12-17 11:35:34

[您有新的未分配科技点]可,可,可持久化!?------0-1Trie和可持久化Trie普及版讲解的相关文章

[您有新的未分配科技点]可,可,可持久化!?------可持久化线段树普及版讲解

最近跑来打数据结构,于是我决定搞一发可持久化,然后发现--一发不可收啊-- 对于可持久化数据结构,其最大的特征是"历史版本查询",即可以回到某一次修改之前的状态,并继续操作:而这种"历史版本查询"会衍生出其他一些强大的操作. 今天,我们主要讲解可持久化线段树.其实,它的另外一个名字"主席树"似乎更加为人所知(主席%%%). 主席树与普通的线段树相比,多出来的操作是在修改时复制修改的一条链,这个操作的过程大概长下面这样. 至于为什么要这样做-- 对

[您有新的未分配科技点]博弈论进阶:似乎不那么恐惧了…… (SJ定理,简单的基础模型)

这次,我们来继续学习博弈论的知识.今天我们会学习更多的基础模型,以及SJ定理的应用. 首先,我们来看博弈论在DAG上的应用.首先来看一个小例子:在一个有向无环图中,有一个棋子从某一个点开始一直向它的出点移动,双方轮流操作,无法操作者输,问是否先手必胜. 考虑一下我们之前的Nim游戏,如果我们把后继状态看成后继点的话,不难发现Nim游戏的互相转移也是一个DAG.因此,DAG上出度为0的点的sg值为0,再用上一篇博客提到的mex操作来求每个点的值就可以了(注意,这并不是一个"大"子图,不能

[您有新的未分配科技点]数位dp:从懵X到板子

数位dp主要用来处理一系列需要数数的问题,一般套路为"求[l,r]区间内满足要求的数/数位的个数" 要求五花八门--比如"不出现某个数字序列","某种数的出现次数"等等-- 面对这种数数题,暴力的想法是枚举每个数,判断是否满足条件 比如这样: #include<cstdio> using namespace std; typedef long long LL; LL l,r,cnt; int main() { scanf("

[您有新的未分配科技点]无旋treap:从好奇到入门(例题:bzoj3224 普通平衡树)

今天我们来学习一种新的数据结构:无旋treap.它和splay一样支持区间操作,和treap一样简单易懂,同时还支持可持久化. 无旋treap的节点定义和treap一样,都要同时满足树性质和堆性质,我们还是用rand()来实现平衡 而无旋treap与treap不同的地方,也是其核心,就是它不旋转用两个新的核心函数:merge函数(合并两棵子树)和split函数(分裂出某棵树的前k个节点,并且作为一棵树返回) 首先看merge函数,它是一个递归实现的过程,先看代码: 1 Treap *merge(

[您有新的未分配科技点]数位DP:从板子到基础

只会统计数位个数或者某种"符合简单规律"的数并不够--我们需要更多的套路和应用 数位dp中常用的思想是"分类讨论"思想.下面我们就看一道典型的分类讨论例题 1026: [SCOI2009]windy数 Time Limit: 1 Sec  Memory Limit: 162 MB Description windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道,在A和B之间,包括A和B,总共有多少个wind

[您有新的未分配科技点][BZOJ3545&amp;BZOJ3551]克鲁斯卡尔重构树

这次我们来搞一个很新奇的知识点:克鲁斯卡尔重构树.它也是一种图,是克鲁斯卡尔算法求最小生成树的升级版首先看下面一个问题:BZOJ3545 Peaks. 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走. 现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1.N<=1e5,M,Q<=5*1e5 上面这个题没有要求在线,因此我们可以离线构造

[您有新的未分配科技点]计算几何入门(1):点,向量以及向量的简单应用

在打了一阵数据结构之后,老师表示"今天晚上让学长给你们讲一下计算几何"--然后就死了.jpg 昨天晚上一直在推数学的式子以及回顾讲课的笔记--计算几何特点就是多而杂,即使是入门部分也是如此-- 首先,我们从二维的几何问题开始处理. 我们知道,高中解析几何计算几何的基础是向量(Vector)和点(Point),所以我们先来表示这两个概念: 在计算几何中,点和向量一般用结构体来存储,像这样: 1 struct Point 2 { 3 double x,y,rad; 4 Point(doub

【您有新的未分配天赋点】计算几何:从被纸笔支配的恐怖到达被代码支配的恐怖

开坑时间:2017/8/5 21:25 今天呢$lgl$神犇终于打开了坑害了无数英雄好汉的新陷阱的盖子新世界的大门:计算几何.尽管再不愿意但该来的还是要来.于是我就这么踏上了这条贼船--(缓更,勿催) 本文将对现阶段所需的四项知识做出一些主观的感性分析,希望大家能够就此感性理解一下. 一.凸包 凸包,顾名思义就是把给定点围在里面的最小凸多边形.这个东西一般采用扫描方法进行处理,时间瓶颈为排序的$O(nlogn)$. 链接:http://www.cnblogs.com/Loser-of-Life/

【您有新的未分配天赋点】网络流:从懵逼到完全懵逼

今天呢@assassain julao讲了一个在OI中极其重要,极其有趣,把无数人坑退役的知识点:网络流. 网络流呢顾名思义,就是在一个图中边有流量的限制,并根据这些流量限制做一些跟这个有关的事(ti)情(mu).什么,范围?按zzh神犇的话来说,就是考试中那些看上去像是dp却又推不出式子的问题的通用解法. 按照问题的倾向,我们将问题分为三类:最大流.最小割.最小费用流. 大家看好我要开始口胡了 首先我们研究最大流,介绍最大流大部分解法原理,增广路定理:只要存在增广路,流就可以继续增大.证明显而