//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\n", query(root[r - 1], s[n] ^ x, l - 1));
}
}
return 0;
}