• 可持久化01Trie


    例题:

    解释:

    首先这里要求连续异或,所以存储前缀异或和数组。首先的话,我们只考虑前r个版本的Trie,所以以root[r]为根节点的Trie就是1到r位置数。但是,还有一个l左端点,所以我们对于每一个节点来说,额外存储一下max_id,表示的就是以当前节点为子树的最大的i。那这个有什么用呢?

    这里我们都是动态开点,也就是假设当前我们的数是1,所以我们要向下遍历找到一个0,由于我们是有限制条件的,所以对于每一个节点,加入他的0的这个儿子的max_id大于我们的限制条件,也就是这个子树里面存在一个s{i],它的下标大于或等于我们的限制条件,所以我们就可以往0这个方向走。

    动态开点思路:对于每一个s[i]我们都去开这个数的01串,也就是一个链子,刚开始是我们的根节点,也就是我们的版本(第几版)。假设当前数是(二进制:101010),我们先开了一个根节点(第几版),于是我们继续开一个1这个节点,如果它的上一个版本中的根节点有0这个儿子,那么我们就让上一个版本的0这个儿子指向当前的根节点(也就是直接拷贝)。然后继续开点,开0这个节点,只要遇到不同的,就直接拷贝,如果上一个版本存在当前的链子的前半部分,我们也是必须开的,只有不同的我们才会去复制。

    那么这样执行下来,就会存下每一个版本的Trie,也就是我们的可持久化Trie,那么这到底有什么用呢?下面就用上面这个例题来介绍它的用处!

    由于当前是由区间限制的,不像一组数据中找两个数他们的异或值最大,没有区间限制的话,用我们的Trie就可以了。首先如果我们只考虑1到r这个条件的话,那么直接从第r个版本的Trie开始找一个数就可以了。然后在考虑数必须大于等于l这个条件,由于我们存储了max_id,也就是下一个节点的max_id,只要大于等于限制条件,就可以遍历这个节点。假设max_id是3,限制条件是2,表示的是这个字数有一个数它的下标是3,所以就可以遍历这个节点,反之,就不可以遍历,因为每一个节点它的下标大于限制条件,都是l前面的数,所以就不能遍历。

    所以,直接上代码:

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. const int N = 600010,M = N*25;
    6. int n, m;
    7. int tr[M][2], idx;
    8. int root[N];
    9. int s[N];
    10. int max_id[M];
    11. void insert(int i, int k, int p, int q)
    12. {
    13. if (k < 0)
    14. {
    15. max_id[q] = i;
    16. return ;
    17. }
    18. int u = s[i] >> k & 1;
    19. if (p)tr[q][u^1] = tr[p][u^1];
    20. tr[q][u] = ++idx;
    21. insert(i, k - 1, tr[p][u], tr[q][u]);
    22. max_id[q] = max(max_id[tr[q][0]],max_id[tr[q][1]]);
    23. }
    24. int query(int root, int limit, int c)
    25. {
    26. int p = root;
    27. for (int i = 23; i >= 0; i--)
    28. {
    29. int u = c >> i & 1;
    30. if (max_id[tr[p][!u]] >= limit && tr[p][!u])p = tr[p][!u];
    31. else p = tr[p][u];
    32. }
    33. return c ^ s[max_id[p]];
    34. }
    35. int main()
    36. {
    37. cin >> n >> m;
    38. root[0] = ++idx;
    39. insert(0,23,0,root[0]);
    40. for (int i = 1; i <= n; i++)
    41. {
    42. int x;
    43. scanf("%d", &x);
    44. root[i] = ++idx;
    45. s[i] = s[i - 1] ^ x;
    46. insert(i, 23, root[i - 1], root[i]);
    47. }
    48. char op[2]; int l, r, x;
    49. while (m--)
    50. {
    51. scanf("%s", op);
    52. if (*op == 'A')
    53. {
    54. scanf("%d", &x);
    55. n++;
    56. root[n] = ++idx;
    57. s[n] = s[n - 1] ^ x;
    58. insert(n, 23, root[n - 1], root[n]);
    59. }
    60. else
    61. {
    62. scanf("%d%d%d", &l, &r, &x);
    63. printf("%d\n", query(root[r - 1], l - 1, s[n] ^ x));
    64. }
    65. }
    66. }

  • 相关阅读:
    Linux:进程(二)
    数字通信世界杂志数字通信世界杂志社数字通信世界编辑部2022年第6期目录
    nbcio-boot移植到若依ruoyi-nbcio平台里一formdesigner部分(一)
    Java开发学习(四十三)----MyBatisPlus查询语句之查询投影
    【云原生】Kubernetes----Rancher助力Kubernetes监控
    企业数字化转型需要深入研究,不能为了转型而转型
    第十章 异常
    【MySQL】数据库基础
    [NLP] LLM---<训练中文LLama2(一)>训练一个中文LLama2的步骤
    WPF三方UI库全局应用MessageBox样式(.NET6版本)
  • 原文地址:https://blog.csdn.net/wsywsyyy/article/details/134319292