给定一个非负整数序列 { a } \{a\} {a},初始长度为 n n n。
有 m m m 个操作,有以下两种操作类型:
A x
:添加操作,表示在序列末尾添加一个数
x
x
x,序列的长度
n
+
1
n+1
n+1。Q l r x
:询问操作,你需要找到一个位置
p
p
p,满足
l
≤
p
≤
r
l \le p \le r
l≤p≤r,使得: $第一行包含两个整数
N
,
M
N,M
N,M,含义如问题描述所示。
第二行包含
N
N
N个非负整数,表示初始的序列$ A$ 。
接下来
M
M
M行,每行描述一个操作,格式如题面所述。
假设询问操作有 T T T 个,则输出应该有 T T T 行,每行一个整数表示询问的答案。
5 5
2 6 4 3 6
A 1
Q 3 5 4
A 4
Q 5 7 0
Q 3 6 6
4
5
6
对于测试点
1
−
2
1-2
1−2,
N
,
M
≤
5
N,M \le 5
N,M≤5。
对于测试点
3
−
7
3-7
3−7,
N
,
M
≤
80000
N,M \le 80000
N,M≤80000。
对于测试点
8
−
10
8-10
8−10,
N
,
M
≤
300000
N,M \le 300000
N,M≤300000。
其中测试点
1
,
3
,
5
,
7
,
9
1, 3, 5, 7, 9
1,3,5,7,9保证没有修改操作。
0
≤
a
[
i
]
≤
1
0
7
0 \le a[i] \le 10^7
0≤a[i]≤107。
这是一道可持久化trie树模板题,函数直接照着敲就行了。
#include
using namespace std;
const int N=6e5+10,M=50*N;
int s[N];
int tr[M][2],max_id[M];
int root[N],idx;
void insert(int k, int p, int q)//我这里把insert改成了迭代版. 这里可以更清晰看到, max_id其实就是当前”trie新加的部分”对应在s数组的位置
{
max_id[q]=k;
for(int i=23;i>=0;i--)
{
int v=s[k]>>i&1;
if(p)tr[q][v^1]=tr[p][v^1];
tr[q][v]=++idx;
max_id[tr[q][v]]=k;
q=tr[q][v];
p=tr[p][v];
}
}
void insert(int i, int k, int p, int q)
{
if(k<0)
{
max_id[q]=i;
return ;
}
int v=s[i]>>k&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]);
max_id[q] = max(max_id[tr[q][0]], max_id[tr[q][1]]);
}
int query(int p, int l, int c)
{
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()
{
int n, m;
scanf("%d%d", &n, &m);
s[0] = 0;
max_id[0] = -1;
root[0] = ++ idx;
insert(0, 0, root[0]);
for(int i = 1; i <= n; i ++)
{
int x;
scanf("%d", &x);
root[i] = ++ idx;
s[i] = s[i - 1] ^ x;
insert(i, root[i - 1], root[i]);
}
while(m --)
{
char op[2];
scanf("%s", op);
if(*op == 'A')
{
int x;
scanf("%d", &x);
n ++;
root[n] = ++ idx;
s[n] = s[n - 1] ^ x;
insert(n, root[n - 1], root[n]);
}
else
{
int l, r, x;
scanf("%d%d%d", &l, &r, &x);
printf("%d\n", query(root[r - 1], l - 1, s[n] ^ x));
}
}
return 0;
}
我太蒻了,所以不能给大家详细的分析,请谅解······