//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; }