题意: 给出nnn个数,两个操作,第一个操作是在末尾加上一个数,数列长度变为n+1n+1n+1,第二个操作是给出左右范围l,rl,rl,r让你从lll到rrr内挑几个数,使他们的异或和是最大的。但给出的数字是加密的,解密需要在第一个操作异或lastanslastanslastans,第二个操作l=(l⊕lastans)%n+1l=(l \oplus lastans)\%n+1l=(l⊕lastans)%n+1,rrr也一样。lastans初始是0,然后是第二个操作的答案。
思路: 肯定不能暴力使用线性基,直接T飞了,所以转换一下思路,给线性基一个位置,这个位置说明是这个位置数的线性基,然后再求最大,pos数组记录位置就好了。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int t, n, m, xxj[N][31], pos[N][31];
void add(int n, int val) {
int net = n;
for(int i = 0; i < 31; i++)
xxj[n][i] = xxj[n - 1][i], pos[n][i] = pos[n - 1][i];
for(int i = 30; i >= 0; i--) {
if(val & (1 << i)) {
if(!xxj[n][i]) {
xxj[n][i] = val;
pos[n][i] = net;
return;
}
if(net > pos[n][i]) {
swap(xxj[n][i], val);
swap(pos[n][i], net);
}
val ^= xxj[n][i];
}
}
}
int main() {
ios::sync_with_stdio(0);
cin >> t;
while(t--) {
cin >> n >> m;
for(int x, i = 1; i <= n; i++)
cin >> x, add(i, x);
int lst = 0, f, a, b;
while(m--) {
cin >> f;
if(f) {
cin >> a;
a ^= lst;
add(++n, a);
} else {
cin >> a >> b;
a = (a ^ lst) % n + 1, b = (b ^ lst) % n + 1;
if(a > b)
swap(a, b);
int ans = 0;
for(int i = 30; i >= 0; i--)
if(pos[b][i] >= a && ((ans ^ xxj[b][i]) > ans))
ans ^= xxj[b][i];
cout << ans << endl;
lst = ans;
}
}
}
return 0;
}