2019 杭电多校1 - 1002 Operation
题目链接:hdu 6579
题解:
贪心地维护序列的前缀线性基 (上三角形态),对于每个线性基,将出现位置靠右的数字尽可能地放在高位,也就是说在插入新数字的时候,要同时记录对应位置上数字的出现位置,并且在找到可以插入的位置的时候,如果新数字比位置上原来的数字更靠右,就将该位置上原来的数字向低位推。
在求最大值的时候,从高位向低位遍历,如果该位上的数字出现在询问中区间左端点的右侧且可以使答案变大,就异或到答案里。
对于线性基的每一位,与它异或过的线性基更高位置上的数字肯定都出现在它右侧 (否则它就会被插入在那个位置了),因此做法的正确性显然。
代码:
#include <bits/stdc++.h>
#define LL long long
#define LD long double
#define ULL unsigned long long
#define UI unsigned int
#define PII pair<int,int>
#define MPII(x,y) pair<int,int>{x,y}
#define _for(i,j,k) for(int i=j;i<=k;i++)
#define for_(i,j,k) for(int i=j;i>=k;i--)
#define efor(i,u) for(int i=head[u];i;i=net[i])
#define lowbit(x) (x&-x)
#define ls(x) x<<1
#define rs(x) x<<1|1
#define inf 0x3fffffff
//#pragma comment(linker, "/STACK:10240000000,10240000000")
using namespace std;
const int maxn = 1e6 + 5;
const int M = 1e9 + 7;
inline int mad(int a,int b){return (a+=b)>=M?a-M:a;}
struct LB{
int a[31],b[31];
void init(){
for(int i=30;i>=0;i--) a[i]=0;
}
void insert(int x,int ind){
for(int i=30;i>=0;i--){
if(x&(1ll<<i)){
if(a[i]){
if(ind>b[i]){
swap(a[i],x);
swap(b[i],ind);
}
x^=a[i];
}
else{
a[i]=x;
b[i]=ind;
break;
}
}
}
}
int maxSum(){
int ret=0;
for(int i=30;i>=0;i--){
if(ret&(1ll<<i)) continue;
ret^=a[i];
}
return ret;
}
};
LB lb[maxn];
int n,m,ta[maxn],t;
int query(int l,int r){
int ret=0;
for(int i=30;i>=0;i--){
if(lb[r].b[i]>=l&&(ret^lb[r].a[i])>ret){
ret^=lb[r].a[i];
}
}
return ret;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>t;
while(t--){
cnt=0;
cin>>n>>m;
_for(i,1,n){
cin>>ta[i];
}
_for(i,1,n){
lb[i]=lb[i-1];
lb[i].insert(ta[i],i);
}
int op,l,r,lastans=0;
_for(i,1,m){
cin>>op;
if(op){
cin>>ta[++n];
ta[n]^=lastans;
lb[n]=lb[n-1];
lb[n].insert(ta[n],n);
}
else{
cin>>l>>r;
l = (l^lastans)%n + 1;
r = (r^lastans)%n + 1;
if(l>r) swap(l,r);
lastans=query(l,r);
cout<<lastans<<"\n";
}
}
}
return 0;
}