大致题意
给长度为n的数列,m次操作(n<=5e5,m<=5e5,ai<(1<<30))
操作0:给x,y,查询区间[x,y]中选若干数异或和的最大值。
操作1:在n+1的位置添加数字x,数组长度n=n+1;
强制在线。
思路
打重现的时候,一看区间异或最大值,复杂度也没算,直接写了个假算法:线段树维护区间线性基…,后来优化了半天一直TLE,后来写写复杂度发现是30logn(n+m)的…giaogiao
后来想了想有没有前缀和线性基啊…然后就没想出来
结果还真有…主要还是对线性基的理解不够深刻。
前缀线性基,开两个数组,ji[maxn][30],pos[maxn][30],一个存前i个的线性基,另一个存每个线性基是来自哪个下标。建立线性基的标准就是,从到小,每一位都用尽量靠右的数去当基。然后查询的时候只要多判断当前基来自的下标是否大于等于l。
这个题看似有插入,实际上其实算是静态的。所以静态的区间异或最大值都可以用前缀线性基来做。
代码
#include<bits/stdc++.h>
using namespace std;
#define maxn 1000005
#define maxm 1000006
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (x*x)
#define inf (ll)2e18+1
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f*x;
}
int t,n,m,ans,a[maxn];
int ji[maxn][30],pos[maxn][30];
void ins(int x,int id)
{
int now=id;
inc(i,0,29){
ji[id][i]=ji[id-1][i];
pos[id][i]=pos[id-1][i];
}
dec(i,29,0){
if(x&(1<<i)){
if(!ji[id][i]){
ji[id][i]=x;
pos[id][i]=now;
break;
}
if(pos[id][i]<now){
swap(ji[id][i],x);
swap(pos[id][i],now);
}
x^=ji[id][i];
}
}
}
int query(int l,int r)
{
int res=0;
dec(i,29,0){
if(pos[r][i]>=l){
res=max(res,res^ji[r][i]);
}
}
return res;
}
int main()
{
t=read();
while(t--){
mem(ji);mem(pos);
n=read();m=read();
inc(i,1,n)ins(read(),i);
ans=0;
inc(i,1,m){
int cmd,x,y;
cmd=read();
if(cmd==0){
x=read();y=read();
x^=ans;y^=ans;
x%=n;y%=n;
x++;y++;
if(x>y)swap(x,y);
ans=query(x,y);
printf("%d\n",ans);
}
else {
x=read();
x^=ans;
ins(x,++n);
}
}
}
return 0;
}
本文介绍了一种解决区间异或最大值问题的高效算法——前缀线性基。通过实例讲解了如何使用前缀线性基进行区间异或最大值的查询和元素插入操作,详细解释了算法的实现细节,并提供了完整的代码示例。
1407

被折叠的 条评论
为什么被折叠?



