题目
进行两种操作,第一种为求区间【L,R】异或最大值,第二种为添加一个数到数列队尾
题解
这两天一直在做线性基的题,原来极为头疼的普通异或问题似乎显得不是那么的难了,但区间异或最大值却仍然对我来说有点难度。
数据结构仍然是线性基(就是线性代数的极大线性无关组,本质非常像),求所有的元素的异或最大值是线性基的模板题,不多解释,首先将问题化简为求【1,R】的区间(前缀)的最大异或,很简单,就是每次添加一个数后都进行一次LB.Max操作就行,然后把这个前缀最大值存起来,现在的问题就变成如何消除【1,L-1】的影响。
在建立线性基时用pos数组记录原数的位置,并且采取贪心的思想,以前插入操作时只要这个位置不是0就跳过直到找到空的位置为止,但现在我们只要找到位置就插入,并且把这个位置原先的值异或后取出,继续向下进行插入操作。这样,最大值值操作时,如果这个i的原先的位置在L的前面,就说明后面的数一定不能线性组合出这个基,所以跳过,只要全部跳过位置在L前面的基,就消除了【1,L-1】的影响
代码
常数太大了,哭了,压时间线AC的,我也不知道自己在写些什么
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e6+7;
struct Linear_Basis{
ll b[63],nb[63];
int pos[63],tot; //b为线性基 nb用来求第K小异或值 tot为nb元素个数
ll n1[maxn][32],n2[maxn][32];
void Init(){ //初始化
tot=0;
memset(b,0,sizeof(b));
}
void Ins(ll x,int x1){ //插入
int R=x1;
for(int i=31;i>=0;i--){
if(x&(1ll<<i)){ //判断x的第i位是否为0
if(!b[i]){ //判断线性基b[i]是否为空
b[i]=x;
pos[i]=x1;
break;
}
else{
if(pos[i]<x1){
swap(b[i],x);
swap(pos[i],x1);
}
}
x^=b[i];
}
}
for(int i=31;i>=0;i--){
n1[R][i]=b[i];
n2[R][i]=pos[i];
}
return ;
}
ll Max(int L,int R){ //求最大值
ll res=0;
for(int i=31;i>=0;i--){
if(n2[R][i]>=L)
res=max(res,res^n1[R][i]);
}
return res;
}
}LB;
int main(){
ll m1,m2,m3;
int T,N,N1,l,r;
cin>>T;
while(T--){
scanf("%d%d",&N,&N1);
LB.Init();
int N2=0,last=0;
for(int i=1;i<=N;i++){
scanf("%d",&m1);
LB.Ins(m1,++N2);
}
for(int i=1;i<=N1;i++){
scanf("%d",&m1);
if(m1==1){
scanf("%d",&m1);
m1 = m1 ^ last;
LB.Ins(m1,++N2);
}
else{
scanf("%d%d",&l,&r);
l = (l ^ last) % N2 + 1;
r = (r ^ last) % N2 + 1;
if(l > r) swap(l, r);
last = LB.Max(l, r);
cout<<last<<endl;
}
}
}
}