• [您有新的未分配科技点]可,可,可持久化!?------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道比较简单的例题:

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

    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
    ",getrank(x+delta)+1);break;
    62             case 4:printf("%d
    ",getval(x)-delta);break;
    63             case 5:printf("%d
    ",getval(getrank(x+delta))-delta);break;
    64             case 6:printf("%d
    ",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

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

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

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

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

  • 相关阅读:
    Python String Methods
    python 文件命名与系统文件同名引起的运行错误
    Python cmd 中文显示乱码
    pyqt4 串口通信 自动化测试
    Python 判断字符串是否包含子字符串
    python time 显示
    pyqt4 UI界面显示乱码
    QtDesigner PyQt4 Python
    FuzzScanner 信息收集小工具
    winrar+目录穿透复现
  • 原文地址:https://www.cnblogs.com/LadyLex/p/7281110.html
Copyright © 2020-2023  润新知