Operation----前缀线性基
operation
题意:给定长度为n的数列和m次操作, 每次在数列末尾添加一个数或者询问区间L到R的子集异或和最大值, 强制在线。 \(n, m\leq 5 \times 10^{5}\)
解法:对所有\(i\epsilon (1, n)\)维护一个\((1,i)\)的线性基\(a[i][32]\), 类似于前缀和的思想, 同时对每个线性基记录一下它每一位最后被插入的位置\(pos[i][32]\)(于是线性基的insert函数就要稍微改一下), 这样在查询\((L, R)\)区间的时候只有\(a[R]\)中pos大于等于\(L\)的位会对\((L, R)\)区间有贡献。
P.S. 如果没有强制在线那么对区间按右端点排序后只需要维护一个线性基就可以了。
P.S.S. 瓜神教会了我前缀线性基 orz
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
int n, m;
struct node{
int p[32], pos[32];
}a[maxn];
void ins(int x) {
n++;
a[n] = a[n - 1];
int pos = n;
for(int i = 31; i >= 0; i--) {
if(x & (1 << i)) {
if(!a[n].p[i]) {
a[n].p[i] = x;
a[n].pos[i] = pos;
return;
}
if(a[n].pos[i] < pos) {
swap(a[n].pos[i], pos);
swap(a[n].p[i], x);
}
x ^= a[n].p[i];
}
}
}
int qmax(int l, int r) {
int res = 0;
for(int i = 31; i >= 0; i--) {
if(a[r].pos[i] < l) continue;
if((a[r].p[i] ^ res)> res) res ^= a[r].p[i];
}
return res;
}
int main() {
int T;
cin >> T;
memset(a[0].p, 0, sizeof(a[0].p));
memset(a[0].pos, 0, sizeof(a[0].pos));
while(T--) {
scanf("%d%d", &n, &m);
int tmp = n;
n = 0;
for(int i = 1; i <= tmp; i++) {
int x;
scanf("%d", &x);
ins(x);
}
int las = 0;
while(m--) {
int f;
scanf("%d", &f);
if(f) {
int x;
scanf("%d", &x);
x ^= las;
ins(x);
}
else{
int l, r;
scanf("%d%d", &l, &r);
l = (l ^ las) % n + 1;
r = (r ^ las) % n + 1;
if(l > r) swap(l, r);
//cout << l << " " << r << '\n';
las = qmax(l, r);
printf("%d\n", las);
}
}
}
return 0;
}