2019杭电多校训练第二场1002
https://vjudge.net/problem/HDU-6579
题意:
给出n个数,接下来有m次操作。
1 k:在数组后面多加一个k。(n=n+1)。
0 l r:询问[l,r]区间内任意取数,异或和的最大值。
特别要注意的是2种操作都加密了,1号操作需要k=k^lasten;2号操作需要l=(l^lasten)%n+1,r=(r^lasten)%n+1.lasten初始值为0,以后lasten为上一次0号操作的答案。
思路:
前缀线性基。建立二维数组pre_p[1000009][maxbit],表示从1~i前i个数组成的线性基。建立数组pre_pos[1000009][maxbit],表示对于第i个前缀线性基第j位为1的数的最大下标(另一种理解: 对于区间[1,i]内的数,二进制第j位为1的最大下标。可能是由不止一个数异或而成的,此时记录这几个数的最小下标)。
对于每一次1号操作,都要在重新维护pre_p数组和pre_pos数组。
对于0号操作,我们按普通的线性基用法使用pre_p[r],不同的就是对于每一个不为0的pre_p[r][j]都要判断pre_pos[r][j]>=l是否成立。即当右端点固定为r时,pre[r][j]是否可以在l以后出现。
(本人原本maxbit开的是32,然后一直血wa,换成31就过了,血的教训)
#include<bits/stdc++.h>
using namespace std;
const int maxbit=31;
#define met(a,b) memset(a,b,sizeof(a))
int p[maxbit+5],pos[maxbit+5],pre_p[1000009][maxbit],pre_pos[1000009][maxbit];
void solve(int val,int adr )//建立1~adr的前缀线性基
{
int k=adr;
for(int i=maxbit-1; i>=0; i--)
{
if((val>>i)&1)//
{
if(!p[i])
{
// cout<<(val>>i)<<endl;
// cout<<i<<"__________ "<<p[i]<<"....."<<val<<endl;
p[i]=val;
pos[i]=adr;
break;
}
else
{
if(pos[i]<adr)swap(p[i],val),swap(adr,pos[i]);//本题重点代码,更新二进制第i为1的下标
val^=p[i];//新的val是原val与原p[i]异或得到的,所以adr应该是2者较小的下标(即原p[i]的下标),所以上面用swap而不是赋值。
}
}
}
for(int i=0; i<=maxbit-1; ++i)
{
pre_p[k][i]=p[i];
pre_pos[k][i]=pos[i];
}
}
int seek_ans(int l,int r)//在区间[l,r]内搜寻答案
{
if(l>r) swap(l,r);
int k=0;
for(int i=maxbit-1; i>=0; i--)
{
if(pre_p[r][i] &&pre_pos[r][i]>=l)
k=max(k,k^pre_p[r][i]);
}
return k;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int T;
cin>>T;
while(T--)
{
int n,m,k;
cin>>n>>m;
for(int i=1; i<=n; i++)
{
cin>>k;
solve(k,i);
}
int lastans=0;
for(int t=0; t<m; t++)
{
int f;
cin>>f;
if(f)
{
cin>>k;
k^=lastans;//解密
solve(k,++n);//建立线性基
}
else
{
int l,r;
cin>>l>>r;
lastans=seek_ans((l^lastans)%n+1,(r^lastans)%n+1);//解密找答案
cout<<lastans<<endl;
}
}
for(int j=maxbit-1;j>=0;--j) p[j]=pos[j]=0;//初始化
for(int i=0;i<=n;i++)
{
for(int j=maxbit-1;j>=0;--j)
pre_p[i][j]=pre_pos[i][j]=0;
}
}
return 0;
}

浙公网安备 33010602011771号