可持久化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 }

 

转载于:https://www.cnblogs.com/mollnn/p/8543595.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值