題意
給你一個長度爲的數列,有次的操作,操作分爲兩種:
- 查詢區間中一些數字的異或最大值。
- 在數組的尾部插入一個數字。
數據範圍
題解
由於線性基的長度很短,不超過30,所以我們可以將數組的前綴線性基保留下來()。而區間的線性基可以由通過添加來獲得。
如果我們已經得到了線性基,那麼久可以查詢前綴區間的子集異或最大值。但是關鍵點是如果在查詢的時候,避免的線性基的干擾(去除在區間中不需要的線性基)。
首先,我們考慮現線性基的插入問題,如果線性基在當前的位置上已經有值了,我們就不能把待插入的值放在這個位置。那麼我們爲了方便處理,我們將每個位置基礎的值爲對應數列的最右側的數字。
具體的操作如下:
我們額外的保存線性基的每一個位置的數在原來數列中的位置。插入的時候,如果對應的位置更靠左,那麼就將待插入的數字和它交換。如果沒有數字,那麼就佔據這個位置。
基於這個策略,我們在查詢區間的時候,可以在區間對應的線性基中去尋找,如果線性基上的位置比要大,那麼就考慮對答案的貢獻,否則就直接跳過這一位。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1e6+10;
int arr[maxn], bc[maxn], pos[maxn], v[maxn][32], p[maxn][32];
void add(int x, int value) {
int t = x ;
for(int i = 30;i>=0;i--) {
if(value &(1<<i)){
if(!bc[i]) {
bc[i] = value;
pos[i] = x;
break;
}
else if (pos[i] < x){
swap(bc[i], value);
swap(pos[i], x);
}
value ^= bc[i];
}
}
for(int i = 0;i<=30;i++) {
v[t][i] = bc[i];
p[t][i] = pos[i];
}
}
int ask(int l ,int r) {
int ans = 0 ;
for(int i = 30;i>=0;i--) {
if(p[r][i] >= l && (ans ^ v[r][i]) > ans) {
ans ^= v[r][i];
}
}
return ans;
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
memset(bc, 0, sizeof(bc));
memset(pos, 0, sizeof(pos));
int n, m ,op ;
scanf("%d %d", &n, &m);
for(int i = 1;i<=n;i++) {
scanf("%d",&arr[i]);
add(i, arr[i]);
}
int last = 0, l, r,value;
while (m--) {
scanf("%d", &op);
if(op) {
scanf("%d",&value);
value ^= last ;
add(++n, value);
} else {
scanf("%d %d", &l,&r);
l = (l^last)%n +1;
r = (r^last) %n + 1;
if(l > r) {
swap(l,r);
}
last = ask(l,r);
printf("%d\n", last);
}
}
}
return 0;
}