P4735 最大异或和 /【模板】可持久化Trie

//tire的可持久化
//线段树的可持久化——主席树
//可持久化的前提:本身的拓扑结构在操作时不变
//可以存下来数据结构的所有历史版本
//核心思想:只记录每一个版本与前一个版本不一样的地方 
//

//s[i]=a[1]^a[2]^a[3]^...^a[n]
//a[p]^a[p+1]^...a[N]^x=s[p-1]^s[N]^x

//对于s[N]^x
//从最高位开始考虑
//如果是1,那么就优先考虑是0的,如果有是0的,那么就把所有当前位为1的舍去,如果没有,再去考虑1的
//如果是0,那么就优先考虑是1的,如果有是1的,那么就把所有当前位位0的社区,如果没有,再去考虑0的

//假设左子树是当前位位0的 
//询问左子树是否存在一个数的下标>=l
//等价于左子树下标最大值是否>=l
//所以可以记录一个max_id,表示当前子树中,下标最大值 
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 600010, M = N * 25;
int n, m;
int s[N];
//01串,所以开2     记录最大的id  
int tr[M][2], max_id[M];
//记录根节点     
int root[N], idx;
//i:当前第几位,前缀和s的下标 
//k:当前处理到第几位
//p:上一个版本
//q:最新版本 
void insert(int i, int k, int p, int q)
{
    //说明处理完最后一位 
    if (k < 0)
    {
        //存的是叶节点,叶节点的max_id就是本身 
        max_id[q] = i;
        return;
    }
    //当前这一位 
    int v = s[i] >> k & 1;
    //如果p存在,就表示之前已经插入的数字中,存在数字这一位存在1  
    if (p)
        //要把其他的信息复制过来,当前儿子不用复制过来,其他的要复制过来 
        tr[q][v ^ 1] = tr[p][v ^ 1]; 
    //当前这一位开新的点 , 
    tr[q][v] = ++ idx;
    //进行下一位 
    insert(i, k - 1, tr[p][v], tr[q][v]);
    //更新最大id 
    max_id[q] = max(max_id[tr[q][0]], max_id[tr[q][1]]);
}
//            根        当前值        左边界限制 
int query(int root, int C, int L)
{
    //从根节点开始 
    int p = root;
    //每一位 
    for (int i = 23; i >= 0; i -- )
    {
        int v = C >> i & 1;
        //判断对立面是否存在 
        if (max_id[tr[p][v ^ 1]] >= L) 
            p = tr[p][v ^ 1];
        else 
            p = tr[p][v];
    }
    return C ^ s[max_id[p]];
}
int main()
{
    scanf("%d%d", &n, &m);
    //根节点  带前缀和之后坐标从0开始
    //初始化为-1 
    max_id[0] = -1;
    //插入x[0]的这个版本 
    root[0] = ++ idx;
    //        从第23位开始,最一开始上个版本没有,所以为0
    //                当前版本为root[0] 
    insert(0, 23, 0, root[0]);
    for (int i = 1; i <= n; i ++ )
    {
        int x;
        scanf("%d", &x);
        //前缀和 
        s[i] = s[i - 1] ^ x;
        //赋根节点 
        root[i] = ++ idx;
        //当前是第i位,从23位开始,前一个根节点,当前根节点 
        insert(i, 23, root[i - 1], root[i]);
    }
    char op[2];
    int l, r, x;
    //操作 
    while (m -- )
    {
        scanf("%s", op);
        //插入 
        if (*op == 'A')
        {
            scanf("%d", &x);
            n ++ ;
            //前缀和 
            s[n] = s[n - 1] ^ x;
            //新版本根节点 
            root[n] = ++ idx;
            //插入 
            insert(n, 23, root[n - 1], root[n]);
        }
        else
        {
            scanf("%d%d%d", &l, &r, &x);
            //要找p-1
            //所以区间是l-1到r-1 
            //限制是l-1 
            printf("%d
", query(root[r - 1], s[n] ^ x, l - 1));
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/QingyuYYYYY/p/12308424.html