题目链接
思路
设 s [ i ] s[i] s[i] 为 a a a 序列的前 i i i 个值得异或和,异或和满足前缀和求区间异或值的性质。
那么每次查询 l , r , x l,r,x l,r,x ,答案即 ∑ i = l r m i n ( S [ n ] ⊕ x ⊕ s [ i − 1 ] ) \sum_{i=l}^{r}min(S[n]\oplus x \oplus s[i-1]) ∑i=lrmin(S[n]⊕x⊕s[i−1])
s [ n ] s[n] s[n]可用数组或单个变量维护
区间
l
,
r
l,r
l,r 内的
a
[
i
]
a[i]
a[i] 用字典树维护,对每个下标建立一颗 字典树,可持久化即可。
用 l a s t last last 数组记录每个节点最后出现时间,那么在如 [ x , y ] [x,y] [x,y] 区间某个节点出现没,只需查询以 y y y 为根节点的字典树, l a s t [ 节 点 ] > x − 1 last[节点] > x-1 last[节点]>x−1 表示出现过。
注意本题额外偏移了 1 1 1,和初始化没有选即 0 0 0 的情况。
代码
#include <bits/stdc++.h>
using namespace std;
int s[600005], id, root[600005], last[600005*25]; // 每条链算上头有25倍长,last记录某个节点最后一次出现的时间
struct tree
{
int nxt[2];
}t[600005*25];
int ins(int x, int val, int la)
{
int res, y; int fq = 1e9;
res = y = ++id;
for(int i = 23; ~i; --i)
{
t[y] = t[x];
last[y] = la;
int tmp = (val>>i)&1;
x = t[x].nxt[tmp];
fq = tmp;
t[y].nxt[tmp] = ++id;
y = t[y].nxt[tmp];
}
last[y] = la;
return res;
}
int query(int limit, int rt, int val)
{
int ans = 0;
for(int i = 23; ~i; --i)
{
int tmp = (val>>i)&1;
if(last[t[rt].nxt[tmp^1]] > limit) // 最后一次出现大于最老出现时间
{
ans |= 1<<i;
rt = t[rt].nxt[tmp^1];
}
else rt = t[rt].nxt[tmp];
}
return ans;
}
int main()
{
int n, m;
scanf("%d%d",&n,&m);
last[0] = -1; // 大于限制才能选,找不到都会到0下标,初始0为最小值min(x-2)
root[0] = ins(root[0],0,0); // 初始都不选
for(int i = 1; i <= n; ++i)
{
int x;
scanf("%d",&x);
s[i] = x^s[i-1];
root[i] = ins(root[i-1],s[i],i);
}
while(m--)
{
char a;
int x, y, z;
scanf(" %c",&a);
if(a == 'A')
{
++n;
scanf("%d",&s[n]);
s[n] ^= s[n-1];
root[n] = ins(root[n-1],s[n],n);
}
else
{
scanf("%d%d%d",&x,&y,&z);
printf("%d\n",query(x-2,root[y-1],z^s[n]));
}
}
return 0;
}