zoukankan      html  css  js  c++  java
  • 可持久化0-1 Trie 简介

    Trie树是字符串问题中应用极为广泛的一种数据结构,可以拓展出AC自动机、后缀字典树等实用数据结构。

    然而在此我们考虑0-1 Trie的应用,即在序列最大异或问题中的应用。

    这里的异或是指按位异或。按位异或有很多重要的性质。比如可拆分性,每个位可以进行单独处理后线性合并得到最终结果。

    同时按位异或也是可减的。比如0111 ^ 1010 = 1101, 那么 1101 ^ 1010 = 0111. 证明从略。


    首先我们考虑0-1 Trie的版本,也就是

    给定一个序列a[i], 每次询问一个数x与a[i]中各元素能得到的按位异或的最大值。

    暴力自然是O(n^2)的。但是我们想到之前的可拆分性,是否能将每个位单独考虑?但是,第一位的选择又会限定第二位的选择范围。即选择第一位是0或1后,第二位的选择就不能从a[i]中的所有元素中进行选择,而要将a[i]分为两份。我们很容易发现这是一个类似树形的问题,所以我们考虑使用树形数据结构。而鉴于多个串根据前缀进行选择性划分的特点,我们使用Trie树来从高位到低位地维护这些0-1串,即0-1 Trie。

    注意到这种从高位到低位的选择一定是全局最优的。也就是说,对于异或结果,从高到低考虑,每一位能设成1就设成1. 证明可以利用反证法。

    这样我们就利用一个贪心完成了这样的事情。这样的处理是O(n+m)的(常数有32倍)


    什么时候需要使用可持久化0-1 Trie呢?我们想,在使用可持久化线段树维护区间K大值得时候,可持久化是否起到了限定区间的作用?同理,在这里,我们也是用可持久化来实现区间的限定。

    重要性质:Trie树的节点存在性满足可减性。

    我们可以把节点的存在性记录改为节点的数目记录,这样用root[r]中某节点的数目减去root[l-1]中某节点的数目,就可以得到区间中是否存在某个节点。


     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 
     4 int ch[1000005][2],val[1000005],cnt[1000005],root[1000005],ts[105],ind,n,m;
     5 
     6 void insert(int p,int p0,int dep) {
     7     ch[p][0]=ch[p0][0];
     8     ch[p][1]=ch[p0][1];
     9     if(dep==30) return;
    10     if(ch[p0][ts[dep+1]]==0) {
    11         ch[p][ts[dep+1]]=++ind;
    12         val[ind]=ts[dep+1];
    13         cnt[ind]=1;
    14         insert(ind,ch[p0][ts[dep+1]],dep+1);
    15     }
    16     else {
    17         ch[p][ts[dep+1]]=++ind;
    18         val[ind]=ts[dep+1];
    19         cnt[ch[p][ts[dep+1]]]=cnt[ch[p0][ts[dep+1]]]+1;
    20         insert(ch[p][ts[dep+1]],ch[p0][ts[dep+1]],dep+1);
    21     }
    22 }
    23 
    24 void trie_insert(int rtx,int num) {
    25     for(int i=1;i<=30;i++)
    26         ts[i]=(num>>(30-i))&1;
    27     insert(root[rtx],root[rtx-1],0);
    28 }
    29 
    30 int xormax(int rtx,int rty,int num) {
    31     int p=root[rtx], q=root[rty], ans=0;
    32     for(int i=29;i>=0;i--) {
    33         if((num>>i)&1) {
    34             if(cnt[ch[q][0]]-cnt[ch[p][0]]) ans=ans*2+1, p=ch[p][0], q=ch[q][0];
    35             else ans=ans*2, p=ch[p][1], q=ch[q][1];
    36         }
    37         else {
    38             if(cnt[ch[q][1]]-cnt[ch[p][1]]) ans=ans*2+1, p=ch[p][1], q=ch[q][1];
    39             else ans=ans*2, p=ch[p][0], q=ch[q][0]; 
    40         }
    41     }
    42     return ans;
    43 }
    44 
    45 int main() {
    46     cin>>n;
    47     for(int i=1;i<=n;i++) {
    48         int t;
    49         cin>>t;
    50         root[i]=++ind;
    51         trie_insert(i,t);
    52     }
    53     cin>>m;
    54     for(int i=1;i<=m;i++) {
    55         int t1,t2,t3;
    56         cin>>t1>>t2>>t3;
    57         t1--;
    58         cout<<xormax(t1,t2,t3)<<endl;
    59     }
    60 }
  • 相关阅读:
    实验证明:ObjectiveC++ 完美支持 ARC
    用 Java 实现的日志切割清理工具
    数字电视,方便了谁
    商品EAN13条码的生成
    关于错误“Cannot connect to the Citrix MetaFrame server.Can't assign requested address”的解决方法
    "加载类型库/dll时出错" 的解决方法
    解决连接SQL Server 2000的TCP/IP错误的Bug
    电脑自动关机之CPU风扇烧坏
    winrar 8 注册方法
    电脑死机之CPU温度过高
  • 原文地址:https://www.cnblogs.com/mollnn/p/8543595.html
Copyright © 2011-2022 走看看