题意:
给出一段长度为n的序列 a[1] ~ a[n]
有以下操作:
0 l r :输出 a[l] ~ a[n] 中的最大异或和
1 x :把 x 加到序列尾,然后令 n = n +1
对于操作0:令 l=(l xor lastans) mod n + 1, r = (r xor lastans) mod n + 1, 若 l>r, 则 swap(l, r)
对于操作1:令 x=x xor lastans
其中 lastans 为上一次操作0输出的答案,其初值为0
分析:
用线性基求区间最大异或和,可以构建一个前缀线性基:d[r][i]
d[r][]表示一个 a[1] ~ a[r] 的线性基
那对于询问怎么保证左区间 l 呢,再设置一个 数组 pos[r][i],表示填入 d[r][i] 的数所在的位置。
构建前缀线性基的方法:
主要是贪心思想:尽量把靠近 r 的数放在高位,这样无论 l 是多少,求得的最大异或和都能尽可能大(高位权值更大)。对于原本的数,应当继续和低位比较,择优存放。
因为后一个线性基就是前一个多插入了一个数,那么插入前应当先继承前一个线性基。
即 令 d[r][i] = d[r-1][i],pos[r][i] = pos[r-1][i],其中1 < i < max_base
最后查询 l ~ r 的最大异或和时,只需要 查询线性基 d[r][],若 pos[r][i] >= l 且 和d[r][i]异或可以令值更大,则和d[r][i]进行异或。
以下代码:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int maxn=1e6+10;
const int base=30;
int d[maxn][base+1],pos[maxn][base+1];
void ins(int x,int r,int p)
{
for(int i=base;i>=0;i--)
{
if(x>>i&1)
{
if(!d[r][i])
{
d[r][i]=x;
pos[r][i]=p;
break;
}
else
{
if(p>pos[r][i]) //d[r][i]已有值,则更靠右的优先放在高位
{
swap(d[r][i],x); //原本的值继续和低位比较(不能直接舍弃)
swap(pos[r][i],p);
}
x^=d[r][i];
}
}
}
}
int query_max(int l,int r)
{
int ret=0;
for(int i=base;i>=0;i--)
{
if(pos[r][i]>=l) //要满足pos[r][i]>=l
ret=max(ret,ret^d[r][i]);
}
return ret;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(d,0,sizeof(d));
memset(pos,0,sizeof(pos));
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
for(int j=base;j>=0;j--) //先继承前一个线性基
{
d[i][j]=d[i-1][j];
pos[i][j]=pos[i-1][j];
}
ins(x,i,i);
}
int ans=0;
while(m--)
{
int op;
scanf("%d",&op);
if(!op)
{
int l,r;
scanf("%d %d",&l,&r);
l=(l^ans)%n+1;
r=(r^ans)%n+1;
if(l>r)
swap(l,r);
ans=query_max(l,r);
printf("%d\n",ans);
}
else
{
int x;
scanf("%d",&x);
x^=ans;
n++;
for(int j=base;j>=0;j--) //先继承前一个线性基
{
d[n][j]=d[n-1][j];
pos[n][j]=pos[n-1][j];
}
ins(x,n,n);
}
}
}
return 0;
}