给你一个序列,两种操作,第一种是求处区间 [ l , r ] [l,r] [l,r]的异或最大值,第二种是在序列后面加上一个值 x x x
首先肯定是线性基来做,但是如果用数据结构来优化的话,是必T的,
题解说的是按照贪心的做法来做,
线性基会用到一个大小为32的数组来记录一个x,但是贪心的做法是
线性基不仅会用到x,还会用到x的位置,并且尽量要把x的位置的值取到最大,
我们用b[i][j]来代表[1,i]这个区间里面的线性基,pos[i][j]来代表这个线性基的位置,这个位置是小于i的第一个
j位为1的数字,
比如在[1,i-1]这个序列中插入一个新的值,那么对于这个值的二进制形式从高到低遍历,如果为1那么就和原来的pos[i-1][j]比较大小,如果pos[i][j] 为什么可以这样,证明一下正确性:
一个线性基,对于每一个 i , a i i,a_i i,ai,有两种可能
1. a i = 0 a_i=0 ai=0并且 只有满足 j > i j>i j>i的 a j a_j aj(即,位于 a i a_i ai后面所有的 a j a_j aj)的第 i i i个二进制可能为1
2. a 1 ! = 0 a_1!=0 a1!=0并且,整个a数组只有 a i a_i ai的第 i i i个二进制位1;
a i a_i ai更高的二进制位( > i >i >i的二进制位)一定为0;
a i a_i ai更低的二进制位( < i <i <i的二进制位)可能为1;
所以当我们有两个数字的第 i i i为都为1,那么贪心的原则使得我们选择位置更大的那个,位置小的那么可以取下面找比它更小的线性基
当我们要求求出 [ l , r ] [l,r] [l,r]的异或最大值是,我们从大到小遍历线性基,选择那些位置 > l >l >l并且使得ans
#include
using namespace std;
#define ll long long
const int maxn = 1e6 + 7;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
typedef pair<int, int> pis;
struct LB{
int b[maxn][32], pos[maxn][32];
void Init() {
memset(b, 0, sizeof(b));
memset(pos, 0, sizeof(pos));
}
void Ins(int x, int w) {
int k = w;
for (int i = 30; i >= 0; i --) {
b[w][i] = b[w-1][i];
pos[w][i] = pos[w-1][i];
}
for (int i = 30; i >= 0; i --) {
if(x >> i) {
if(!b[w][i]) {
b[w][i] = x;
pos[w][i] = k;
return ;
}else if(k > pos[w][i]) {
swap(x, b[w][i]);
swap(pos[w][i], k);
}
x ^= b[w][i];
}
}
}
int getMax(int l, int r) {
int res = 0;
for (int i = 30; i >= 0; i --)
if(pos[r][i] >= l && (res ^ b[r][i]) > res)
res = res ^ b[r][i];
return res;
}
}lb;
int main() {
int T;
scanf("%d", &T);
while(T --) {
int n, m;
scanf("%d %d", &n, &m);
lb.Init();
for (int i = 1, x; i <= n; i ++) {
scanf("%d", &x);
lb.Ins(x, i);
}
int pre = 0;
while(m --) {
int op;
scanf("%d", &op);
if(op) {
int x;
scanf("%d", &x);
lb.Ins(x^pre, ++n);
}else {
int l, r;
scanf("%d %d", &l, &r);
l = (l ^ pre) % n + 1;
r = (r ^ pre) % n + 1;
if(l > r) swap(l, r);
pre = lb.getMax(l, r);
printf("%d\n", pre);
}
}
}
return 0;
}