51nod 1295 XOR key(可持久化trie)

链接

题意:

给出一个长度为N的正整数数组A,再给出Q个查询,每个查询包括3个数,L, R, X (L <= R)。求A[L] 至 A[R] 这R - L + 1个数中,与X 进行异或运算(Xor),得到的最大值是多少?



思路:

初学可持久化字典树,对它的理解还不是很深刻。主席树是可持久化的线段树,两者有相似之处。主席树中有版本这一概念,每一个版本都是新建一棵树,先复制上一个版本的内容,再做一些当前版本特有的修改,当然复制并不是直接复制,只是用一个指针指一下即可。那么同样,对于上面A数组的下标可以用版本表示,对于每个新的版本也要‘复制‘前一个版本的信息,并插入本版本的字符(这里就是0或1了),每插入一个字符都需要记录一下当前版本下的该节点的该字符的数量,就是上一个版本的对应节点的该字符数量加1(可看成前缀和)。这个字符的数量很重要,判断两个版本之间深度为x存不存在某一字符(记为c)需要通过比较两个版本深度为x的节点的c的数量的大小来判断。如果新的版本的要大,那么肯定是l+1到r这些版本中存在一些字符串插入了这个深度为x的c,如果一样的话,就是不存在 ss + c…的字符串了,ss表示深度为x-1时候的结果(假设存在)。

总节点最大差不多是版本数 ∗ * 最长字符串的长度

插入和查询的时间复杂度是O(length)



#include <bits/stdc++.h>

using namespace std;
const int N = 5e4 + 5;

int ch[N * 33][2];//儿子指针
int sz[N * 33];//某一节点的某个字符的数量
int root[N * 33], tot;

void update(int last, int cur, int v) {
    for (int i = 31; i >= 0; i--) {
        ch[cur][1] = ch[last][1];
        ch[cur][0] = ch[last][0];
        sz[cur] = sz[last] + 1;
        int j = 1 & (v >> i);
        ch[cur][j] = ++tot;
        cur = ch[cur][j];
        last = ch[last][j];
    }
    sz[cur] = sz[last] + 1;
}

int query(int last, int cur, int v) {
    int ans = 0;
    for (int i = 31; i >= 0; i--) {
        int j = !(1 & (v >> i));
        if (sz[ch[cur][j]] > sz[ch[last][j]]) {
            ans |= (1 << i);
            last = ch[last][j];
            cur = ch[cur][j];
        } else {
            last = ch[last][!j];
            cur = ch[cur][!j];
        }
    }
    return ans;
}

int main() {
    int n, q;
    scanf("%d%d", &n, &q);
    for (int i = 1, x; i <= n; i++) {
        scanf("%d", &x);
        update(root[i - 1], root[i] = ++tot, x);
    }

    for (int i = 1, x, l, r; i <= q; i++) {
        scanf("%d%d%d", &x, &l, &r);
        printf("%d\n", query(root[l], root[r + 1], x));
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值