前缀线性基
题目信息:
题目连接 10+(60×)
大意: 给一个数列,有两种操作
1.
1.
1. 在数列的末尾加入一个数
2.
2.
2. 给一个区间,询问区间内若干个数字的异或和最大值。强制在线。
思路:
若干个数的最大异或和,显然用线性基来做。但是这里是区间操作,就需要一种类似前缀和的东西来维护当前位的线性基。
考虑维护每个点的前缀线性基,线性基里将靠右的数字尽可能放高位,就是存一个额外存一个位置 p,表示这个位上的数的位置,从高位到低位扫,如果当前位置大于这个位上的位置那么交换,然后就得到了一个靠右的数字尽可能在高位的线性基
然后对于询问 [l,r]
在 r 的前缀线性基里找,只在位置大于等于 l 的位更新答案
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
#define ll long long
const int maxn = 1e6+400;
int d[maxn][32],dt[maxn][32];
int mp[maxn],cnt;
int n,m;
void ad(int tt){
for(int i=0;i<31;++i){
d[tt][i] = d[tt-1][i];
dt[tt][i] = dt[tt-1][i];
}
int x = mp[tt], p = tt;
for(int i=30;i>=0;--i){
if(x&(1<<i)){
if(d[tt][i]){
if(dt[tt][i]<p){
int t;
//碰到了比当前靠前的数交换
//试图让低位也变得更右
t=d[tt][i];d[tt][i]=x;x=t;
t=dt[tt][i];dt[tt][i]=p;p=t;
}
x ^= d[tt][i];
}
else{
d[tt][i] = x;
dt[tt][i] = p;
break;
}
}
}
}
int main(){
int _;scanf("%d",&_);
while(_--){
scanf("%d%d",&n,&m);
cnt = n;
for(int i=1;i<=n;++i){
scanf("%d",&mp[i]);
ad(i);
}
int op,a,b;
int L = 0;
while(m--){
scanf("%d",&op);
if(op){
scanf("%d",&a);
a = a^L;
mp[++cnt] = a;
ad(cnt);
}
else{
scanf("%d%d",&a,&b);
a = (a^L)%cnt+1;
b = (b^L)%cnt+1;
if(a>b){int t=a;a=b;b=t;}
int ans = 0;
for(int i=30;i>=0;--i){
if(dt[b][i]<a) continue;
ans = max(ans,ans^d[b][i]);
}
printf("%d\n",ans);
L = ans;
}
}
}
return 0;
}