2019HDU多校第一场B
题目链接
题意
给你一个序列,有两种操作:
1.求出区间[l,r]的异或最大值;
2.在序列后加一个x。
思路
求异或和的最大值,用线性基来做,关于线性基的定义和常用函数,见我的博客。
此题用贪心+线性基来做,和传统的线性基相比,这里需要维护区间,以及数的位置,所以我们用两个二维数组来表示。v[i][j]代表第i个数为右边界时的线段基,j总是从高位开始更新。pos[i][j]代表数的位置。
详细解释见下列代码:
#include<bits/stdc++.h>
using namespace std;
int v[1000007][32],pos[1000007][32];//二维数组维护右边界,避免当后续数字输入时对前面已定边界的干扰,pos保存数的位置
void insert(int x,int w)
{
int k=w;
for(int i=30;i>=0;i--)//w从1开始增加,可以理解为先更新右区间为1的线段基;然后w取2时,先将现有的线段基复制给v[2][j],
{ //然后在此基础上新增加一个数,更新v[2][j],这样就不会破坏v[1][j],以此类推,直到完成所有线段基的增加更新
v[w][i]=v[w-1][i];
pos[w][i]=pos[w-1][i];//每个数的位置也需要保留,因为数在线段基内位置会变,具体见后面
}
for(int i=30;i>=0;i--)
{
if(x>>i)//现将x的二进制右移i位,相当于x&(1<<i+1)
{
if(!v[w][i])//如果当前位置还没更新就更新
{
v[w][i]=x;
pos[w][i]=k;
return ;
}
else if(k>pos[w][i])//如果新输入的数b位置比原线段基此处数的位置大,那么交换,将原位置的数就行线段基下传处理
{//(因为我们总是希望先找到比l位置大的位置的数,所以优先将位置大的数放后面)
swap(v[w][i],x);
swap(k,pos[w][i]);
}
x^=v[w][i];
}
}
}
int findmax(int l,int r)
{
int ans=0;
for(int i=30;i>=0;i--)//大的数据存在高位,所以找最大值从高位往低位找
{
if(pos[r][i]>=l&&(ans^v[r][i])>ans)
{
ans=ans^v[r][i];
}
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(v,0,sizeof(v));//多组输入,初始化数组
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);
insert(x,i);//当输入新数据时,要将数据传到insert函数,位置也需要记录
}
int op;
int pre=0;//题目最后要求的,用于更新op操作
while(m--)
{
scanf("%d",&op);
if(op)
{
int x;
scanf("%d",&x);
x^=pre;
insert(x,++n);
}
else
{
int l,r;
scanf("%d%d",&l,&r);
l=(l^pre)%n+1;
r=(r^pre)%n+1;
if(l>r)
swap(l,r);
pre=findmax(l,r);
printf("%d\n",pre);
}
}
}
return 0;
}