1. 程式人生 > >可持久化0-1 Trie 簡介

可持久化0-1 Trie 簡介

AI 自然 -c for body else 劃分 容易 處理

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 }

可持久化0-1 Trie 簡介