[数据结构]可持久化01Trie

Nickel_Angel

2019-06-17 21:33:41

Personal

有一个长度为n的序列,我们支持插入操作和询问区间$[L,R]$的最大异或和 朴素的01Trie暴力会导致TLE,于是就有了专门应对区间查询的可持久化01Trie…… 通过对主席树的学习我们发现,可持久化数据结构,其实是在新建版本时新建一个根,对于本次插入,与在本次插入相反的边, 继承上一个点的子树信息,相当于继承了上个版本的前缀…… ![presistable.png](https://i.loli.net/2019/06/21/5d0cdaddc24b874434.png) Code: (这里的$query(L, R, x)$是指求一个$res = \bigoplus_{i = l}^{r}a_i(L \leq l \leq r \leq R)$,使得$res\,xor\,x$最大,$ \bigoplus,xor$均为异或) ```cpp class Trie { private: int ch[maxn * memory_size][2], Size[maxn * memory_size], tot; //memory_size是根据总版本数确定的 public: int Root[maxn], ver;//ver是version,即当前版本编号 inline void clean() { memset(ch, 0, sizeof(ch)); memset(Size, 0, sizeof(Size)); tot = 0; ver = 0; } inline void add(int x) { int lst = Root[ver];//储存上一版本的根节点,以便记录信息 Root[++ver] = tot + 1; //本版本的根节点,由于还未判断其子树的连边情况,故写作tot + 1而非++tot for (int i = max_bit, id; i >= 0; --i)//max_bit即最大的x的位数 { Size[++tot] = Size[lst] + 1; //在循环中正式新建节点,标记长度 id = (x >> i) & 1; ch[tot][id] = tot + 1; ch[tot][!id] = ch[lst][!id];//反向边连向上一版本 lst = ch[lst][id];//在旧版本的子树中游走 } Size[++tot] = Size[lst] + 1;//新建末尾节点,标记长度 } inline void query(int L, int R, int x) { if (L > R) return 0; int nowl = Root[L - 1], nowr = Root[R], res = 0; for (int i = max_bit, id; i >= 0; --i) { id = (x >> i) & 1; if (Size[ch[nowl][!id] < Size[ch[nowr][!id]]) //贪心:如果能走反向边,就走 //判断条件:顺着左边界所在版本走到的点的长度小于右边界走到的点的长度,即保证了经过的点均在给定区间 { res += (1 << i); nowl = ch[nowl][!id]; nowr = ch[nowr][!id]; } else { nowl = ch[nowl][id]; nowr = ch[nowr][id]; } } return res; } }; ``` ~~才不会告诉你上面的代码这是[P4735 最大异或和](https://www.luogu.org/problemnew/show/P4735)的一部分~~