如果你了解过 01 Trie 和 可持久化线段树(例如 : 主席树 )、那么就比较好去可持久化 Trie

可持久化 Trie 当 01 Trie 用的时候能很方便解决一些原本 01 Trie 不能解决的一些问题

01 Trie 的经典贪心算法可以在一个数集里面找出某个数和 X 异或的最值

但若数集不固定、变成了每次问询一段区间或者树上路径此时 01 Trie 便无法快速解决

这个时候需要使用可持久化的 Trie 来维护和进行查询操作、例如用前缀和建 Trie 就能方便查询某一区间的状况

可持久化 Trie 和主席树很类似,都是通过为每个前缀or路径等存储一颗 Trie

然后再通过减法的方式来达到某一区间或者某一历史版本的状态

这里只给出模板、关于这个算法的学习、推荐 ==> Click here

模板 :

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. ;
  4. ;
  5. int root[maxn];///每颗Trie的根节点编号
  6. int sz[maxNode];///每个节点被添加or访问了多少次
  7. ];///静态指针、指向每个节点的01边指向的节点
  8. ;///用于新开节点编号、多测问题别忘初始化
  9.  
  10. ///静态开辟节点
  11. int newNode()
  12. {
  13. memset(ch[totNode], , sizeof(ch[totNode]));
  14. sz[totNode] = ;
  15. return totNode++;
  16. }
  17.  
  18. ///F是将要被继承的树、C是当前新增的树、val就是即将被添加到C这棵树上的值
  19. inline void Insert(int F, int C, int val)
  20. {
  21. F = root[F], C = root[C];
  22. ; i>=; i--){
  23. ;
  24. if(!ch[C][bit]){
  25. ch[C][bit] = newNode();
  26. ch[C][!bit] = ch[F][!bit];
  27. sz[ ch[C][bit] ] = sz[ ch[F][bit] ];
  28. }
  29. C = ch[C][bit], F = ch[F][bit];
  30. sz[C]++;
  31. }
  32. }
  33.  
  34. ///查询函数可以说是很多变了
  35. ///可持久化Trie的查询并不是很模板的一个东西
  36. ///所以请务必理解可持久化Trie的原理再来运用这个模板
  37. ///以下的查询函数是 HDU 4757 的查询函数
  38. int Query(int A, int B, int val)
  39. {
  40. int lca = LCA(A, B);
  41. int lcaAns = arr[lca]^val;
  42. A = root[A], B = root[B], lca = root[lca];
  43. ;
  44. ; i>=; i--){
  45. ;
  46. * sz[ch[lca][!bit]] > ){
  47. ret += <<i;
  48. A = ch[A][!bit];
  49. B = ch[B][!bit];
  50. lca = ch[lca][!bit];
  51. }else A = ch[A][bit], B = ch[B][bit], lca = ch[lca][bit];
  52. }
  53.  
  54. return max(ret, lcaAns);
  55. }
  56.  
  57. ///这个查询函数则对应 BZOJ 3261
  58. int query(int x, int y, int val)
  59. {
  60. ;
  61. ; i--){
  62. ;
  63. )
  64. ret += (<<i),
  65. y = ch[y][!c],
  66. x = ch[x][!c];
  67. else x = ch[x][c], y = ch[y][c];
  68. }
  69. return ret;
  70. }

一些题目 :

BZOJ 3261 最大异或和

分析 : 每次更新都是从最后加一个数、而每次问询都是查询某一后缀的异或和

可持久化 01 Trie 能够很方便知道某一区间的状况、但关键是往区间里面装什么才能方便查询

注意到异或的自反性质、可以每次给每一个前缀以可持久化的方式建立一颗 Trie

然后对于区间 (L, R) 在可持久化 01 Trie 内查询其两个区间与 (PreSum[N] xor x) 的异或最大值便是答案

因为如果某个下标假设为 idx ( L ≤ idx ≤ R ) 且 PreSum[idx] xor (PreSum[N] xor x) 有最大值

那么就说明了 idx 便是这个 p 、因为上面的异或表达式实际上 (1 ~ idx) 这段的异或和都被自反掉了

所以剩下的肯定就是后缀异或和了、所以利用前缀异或和建可持久化 Trie 即可。

  1. #include<bits/stdc++.h>
  2. #define LL long long
  3. #define ULL unsigned long long
  4.  
  5. #define scs(i) scanf("%s", i)
  6. #define sci(i) scanf("%d", &i)
  7. #define scd(i) scanf("%lf", &i)
  8. #define scl(i) scanf("%lld", &i)
  9. #define scIl(i) scanf("%I64d", &i);
  10. #define scii(i, j) scanf("%d %d", &i, &j)
  11. #define scdd(i, j) scanf("%lf %lf", &i, &j)
  12. #define scll(i, j) scanf("%lld %lld", &i, &j)
  13. #define scIll(i, j) scanf("%I64d %I64d", &i, &j)
  14. #define sciii(i, j, k) scanf("%d %d %d", &i, &j, &k)
  15. #define scddd(i, j, k) scanf("%lf %lf %lf", &i, &j, &k)
  16. #define sclll(i, j, k) scanf("%lld %lld %lld", &i, &j, &k)
  17. #define scIlll(i, j, k) scanf("%I64d %I64d %I64d", &i, &j, &k)
  18.  
  19. #define lson l, m, rt<<1
  20. #define rons m+1, r, rt<<1|1
  21. #define lowbit(i) (i & (-i))
  22. #define mem(i, j) memset(i, j, sizeof(i))
  23.  
  24. #define fir first
  25. #define sec second
  26. #define ins(i) insert(i)
  27. #define pb(i) push_back(i)
  28. #define pii pair<int, int>
  29. #define mk(i, j) make_pair(i, j)
  30. #define pll pair<long long, long long>
  31. using namespace std;
  32. <<) + ;
  33. ;
  34. ][], sum[maxn*], sz = ;
  35. int root[maxn];
  36.  
  37. int newNode()
  38. {
  39. memset(ch[sz], , sizeof(ch[sz]));
  40. sum[sz] = ;
  41. return sz++;
  42. }
  43.  
  44. void Insert(int y, int x, int val)
  45. {
  46. ; i--){
  47. ;
  48. if(!ch[x][c]){
  49. ch[x][c] = newNode();
  50. ch[x][!c] = ch[y][!c];
  51. sum[ch[x][c]] = sum[ch[y][c]];
  52. }
  53. x = ch[x][c], y = ch[y][c];
  54. ++sum[x];
  55. }
  56. }
  57.  
  58. int query(int x, int y, int val)
  59. {
  60. ;
  61. ; i--){
  62. ;
  63. )
  64. ret += (<<i),
  65. y = ch[y][!c],
  66. x = ch[x][!c];
  67. else x = ch[x][c], y = ch[y][c];
  68. }
  69. return ret;
  70. }
  71.  
  72. int N, M, arr[maxn], PreSum[maxn];
  73. int main(void)
  74. {
  75. scii(N, M);
  76. arr[] = , N++;
  77. ; i<=N; i++) sci(arr[i]);
  78. ; i<=N; i++) PreSum[i] = PreSum[i-]^arr[i];
  79. ; i<=N; i++){
  80. root[i] = newNode();
  81. Insert(root[i-], root[i], PreSum[i]);
  82. }
  83.  
  84. ];
  85. int l, r, x;
  86. while(M--){
  87. scs(ch);
  88. ] == 'A'){
  89. N++;
  90. sci(arr[N]);
  91. PreSum[N] = PreSum[N-]^arr[N];
  92. root[N] = newNode();
  93. Insert(root[N-], root[N], PreSum[N]);
  94. }else{
  95. sciii(l, r, x);
  96. ], root[r], PreSum[N]^x);
  97. printf("%d\n", ans);
  98. }
  99. }
  100. ;
  101. }

HDU 4757 Tree

题意 : 给出一颗树、树上的节点都有权值、接下来给出若干个询问 (u、v、x)

问从 u 到 v 最短路径上哪个节点和 x 异或结果最大、输出这个结果

分析 : 和上一题有点类似、只不过这里的区间变成了 u 到 v 间的最短路径即树上路径

嘚想办法建出和上题一样类似 "前缀和" 的东西方便使用减法来查询历史版本

针对树上路径的自然联想到 LCA 如果先随意指定一个根节点、先变成一颗有根树

那么 u 到 v 的最短路径可以用 LCA 表示为 dist(u) + dist(v) - 2 * dist(LCA(u, v))

根据上面这条式子、可以得到一个启发、可持久化 Trie 也可以通过这种方法来得到

我们需要的树上路径 Trie 即可以理解为从 u 到 v 最短路径中所有节点的组成 Trie

那做法就是先指定一个根、然后从根开始DFS、每次遍历到一个新节点

便以可持久化的方式新建一颗 Trie 且是继承自其父亲节点的 Trie

那么在查询的时候、给出两个节点 u、v 就对于每一位就可以通过

sz(u) + sz(v) - 2 * sz(LCA(u, v)) 来判断某一位是否包含 0/1 从而进行贪心选择

最后得到异或最值、当然注意这样做会漏掉 LCA、最后只要和 LCA ^ x 取最大便是答案

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. ;
  4. ];
  5. int Head[maxn], cnt;
  6. int dep[maxn], maxDep;
  7. ];
  8. int arr[maxn];
  9. int n, m;
  10.  
  11. ;
  12. ];
  13. ;
  14.  
  15. inline void init_Graph()
  16. {
  17. memset(Head, -, sizeof(Head));
  18. memset(Fa, , sizeof(Fa));
  19. cnt = ; maxDep = ;
  20. }
  21.  
  22. inline void AddEdge(int from, int to)
  23. {
  24. Edge[cnt].v = to;
  25. Edge[cnt].nxt = Head[from];
  26. Head[from] = cnt++;
  27. }
  28.  
  29. int newNode()
  30. {
  31. memset(ch[tot], , sizeof(ch[tot]));
  32. sz[tot] = ;
  33. return tot++;
  34. }
  35.  
  36. inline void Insert(int F, int C, int val)
  37. {
  38. F = root[F], C = root[C];
  39. ; i>=; i--){
  40. ;
  41. if(!ch[C][bit]){
  42. ch[C][bit] = newNode();
  43. ch[C][!bit] = ch[F][!bit];
  44. sz[ ch[C][bit] ] = sz[ ch[F][bit] ];
  45. }
  46. C = ch[C][bit], F = ch[F][bit];
  47. sz[C]++;
  48. }
  49. }
  50.  
  51. void dfs(int v, int fa)
  52. {
  53. root[v] = newNode();
  54. Insert(fa, v, arr[v]);
  55. ] != ) maxDep = max(maxDep, dep[v] = dep[Fa[v][]]+);
  56. ; i=Edge[i].nxt){
  57. int Eiv = Edge[i].v;
  58. ]) continue;
  59. Fa[Eiv][] = v;
  60. dfs(Eiv, v);
  61. }
  62. }
  63.  
  64. inline void Doubling()
  65. {
  66. ));
  67. ; j<=UP; j++){
  68. ; i<=n; i++){
  69. ] != )
  70. Fa[i][j] = Fa[Fa[i][j-]][j-];
  71. }
  72. }
  73. }
  74.  
  75. int LCA(int u, int v)
  76. {
  77. ));
  78. if(dep[u] < dep[v]) swap(u, v);
  79. ; j--)
  80. && dep[Fa[u][j]] >= dep[v])
  81. u = Fa[u][j];
  82.  
  83. if(u == v) return v;
  84.  
  85. ; j--){
  86. if(Fa[u][j] != Fa[v][j]){
  87. u = Fa[u][j];
  88. v = Fa[v][j];
  89. }
  90. }
  91.  
  92. ];
  93. }
  94.  
  95. int Query(int A, int B, int val)
  96. {
  97. int lca = LCA(A, B);
  98. int lcaAns = arr[lca]^val;
  99. A = root[A], B = root[B], lca = root[lca];
  100. ;
  101. ; i>=; i--){
  102. ;
  103. * sz[ch[lca][!bit]] > ){
  104. ret += <<i;
  105. A = ch[A][!bit];
  106. B = ch[B][!bit];
  107. lca = ch[lca][!bit];
  108. }else A = ch[A][bit], B = ch[B][bit], lca = ch[lca][bit];
  109. }
  110.  
  111. return max(ret, lcaAns);
  112. }
  113.  
  114. int main(void)
  115. {
  116. while(~scanf("%d %d", &n, &m)){
  117. ; i<=n; i++) scanf("%d", &arr[i]);
  118.  
  119. init_Graph();
  120. ; i<n; i++){
  121. int u, v;
  122. scanf("%d %d", &u, &v);
  123. AddEdge(u, v);
  124. AddEdge(v, u);
  125. }
  126.  
  127. tot = ;
  128. dfs(, );
  129. Doubling();
  130.  
  131. while(m--){
  132. int u, v, x;
  133. scanf("%d %d %d", &u, &v, &x);
  134. printf("%d\n", Query(u, v, x));
  135. }
  136. }
  137. ;
  138. }

可持久化Trie模板的更多相关文章

  1. 算法笔记--字典树(trie 树)&& ac自动机 && 可持久化trie

    字典树 简介:字典树,又称单词查找树,Trie树,是一种树形结构,是哈希树的变种. 优点:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较. 性质:根节点不包含字符,除根节点外每一个 ...

  2. [BZOJ5338][TJOI2018]xor(可持久化Trie)

    可持久化Trie模板题. 建两种可持久化Trie,每个点两棵,一棵对DFS求前缀和,一棵对祖先求前缀和. 或者树剖,不好写多少还多个log. #include<cstdio> #inclu ...

  3. 可持久化trie 学习总结

    QAQ 以前一直觉得可持久化trie很难,今天强行写了一发觉得还是蛮简单的嘛 自己的模板是自己手写的,写了几道题目并没有出过错误 THUSC的第二题的解法五貌似就是可持久化trie,时间复杂度O(60 ...

  4. bzoj4546-codechef XRQRS(可持久化Trie)

    中文题题意我就不说了 解析: 可持久化Trie的模板题,详见注释 代码 #include<cstdio> #include<cstring> #include<strin ...

  5. 可持久化 trie 的简单入门

    可持久化 $trie$  ....又是一个表里不一的东西..... 可持久化 $trie$  的介绍: 和主席树类似的,其实可持久化就是体现在前缀信息的维护上(搞不懂这怎么就叫做可持久化了...) $ ...

  6. BZOJ.5338.[TJOI2018]xor(可持久化Trie)

    BZOJ LOJ 洛谷 惊了,18年了还有省选出模板题吗= = 做这题就是练模板的,我就知道我忘的差不多了 询问一就用以DFS序为前缀得到的可持久化Trie做,询问二很经典的树上差分. 注意求询问二的 ...

  7. 51nod 1295 XOR key (可持久化Trie树)

    1295 XOR key  题目来源: HackerRank 基准时间限制:1.5 秒 空间限制:262144 KB 分值: 160 难度:6级算法题   给出一个长度为N的正整数数组A,再给出Q个查 ...

  8. [BZOJ3261&BZOJ3166]可持久化trie树及其应用

    可持久化trie树 可持久化trie树现在想来是比较好理解的了,但却看了一个下午... 相当于对于每个状态建立一条链(或者说一棵trie),求解的时候只要让两个点按照相同的步子走然后看sum的大小关系 ...

  9. [note]可持久化Trie

    可持久化Trie 参考可持久化线段树的思想,修改的时候先直接复制,再对需要修改的点新建节点 可持久化Trie也是同样的做法,假设现在需要在原本Trie的基础上插入一个字符串 先把上个Trie的对应节点 ...

随机推荐

  1. 【VS开发】循序渐进学习使用WINPCAP(一)

    winpcap教程 中文教程 http://www.ferrisxu.com/WinPcap/html/index.html 除此之外, WinPcap · Developer Resources下载 ...

  2. spring boot-12.Servlet 容器

    1.spring boot 默认使用的是嵌入式的Servlet容器,spring-boot-starter-web 依赖了spring-boot-satrter-tomcat就是引入了嵌入式的tomc ...

  3. [转帖]Linux下批量替换文件内容方法

    Linux下批量替换文件内容方法 https://www.cnblogs.com/fjping0606/p/4428850.html 刚才用到的命令 原作者写的挺好的记录一下 以后 用. 1:查找fi ...

  4. Java 架构师面试题

    基础题目 Java线程的状态 进程和线程的区别,进程间如何通讯,线程间如何通讯 HashMap的数据结构是什么?如何实现的.和HashTable,ConcurrentHashMap的区别 Cookie ...

  5. 枚举java语言中的修饰符组合

    package model; /*22:37 2019/7/20*/ /* top class的修饰符组合 abstract final public 2 * 2 * 2 warning: abstr ...

  6. Nginx 故障实例

    linux vi 操作提示 Found a swap file by the name "/usr/local/nginx/conf/.nginx.conf.swp" 解决方法: ...

  7. saiku数据实现实时更新

    (1) # vim saiku-server/tomcat/webapps/ROOT/js/saiku/Settings.yaml 将 LOCALSTORAGE_EXPIRATION: 3600000 ...

  8. python编码环境安装与基本语法

    一.pycharm的基本使用 1.python以及pycharm的安装 python的版本选择:3.x版本就行 pycharm的版本选择:社区版就够用 pycharm只是一个编写工具,python才是 ...

  9. (转) MiniUI使用

    来源于https://my.oschina.net/yunsy/blog/542597 1.MiniUI页签定位 <body> <input name = "bizType ...

  10. SpringFramework中的BeanWrapper丶PropertyEditor

    BeanWrapper是org.springframework.beans包下的一个借口,对应的实现类为BeanWrapperImpl,提供对应的get/set方法,并且设置属性的可读性和可写性. p ...