链接: B - Operation
题意:
给你 n 个数和 m 次操作,操作有两种类型 :
操作一 : 表示在区间[l , r]中找出任意个数,使这些数的异或和最大。
操作二 : 在数组末尾插入一个数x。
思路 :
- 对于区间异或和最大的问题,首先要想到线性基。我们可以先了解一下什么是 线性基。
- 我们可以维护一个前缀线性基 , 用val[i][j] ,表示区间[1 , i]的第 j 位线性基的值。用 pos[i][j] 表示区间[1 , i]的第 j 位线性基的位置,如果我们贪心的让pos[i][j]的值尽可能的大,那么我们在最后查询答案的时候 如果 pos[r][i] ≥ \geq ≥ l ,那就说明可以选择第 i 位的线性基。
- 如何让这个位置尽可能靠右呢,在构造的时候,如果当前的val[i][j],已经有值了,单我当前插进来的位置比原来的更靠右,那我就可以把原来的线性基替换下来,然后让替换下来的这个值继续去匹配后面的线性基。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6 + 7;
const int mod = 998244353;
int n,m,T;
int val[maxn][31],pos[maxn][31],a[maxn];
void add(int now, int x){
for(int i = 0; i <= 30; i ++){
val[now][i] = val[now - 1][i]; //前缀
pos[now][i] = pos[now - 1][i];
}
int np = now;
for(int i = 30; i >= 0; i --){
if((x >> i) & 1){
if(val[now][i]){
if(np > pos[now][i]){ //当前位置更加靠右
swap(pos[now][i] , np); //替换两个值的位置
swap(val[now][i] , x);
}
x ^= val[now][i];
}
else{
val[now][i] = x;
pos[now][i] = np;
break;
}
}
}
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
memset(val, 0 , sizeof(val));
memset(pos, 0 , sizeof(pos));
for(int i = 1; i <= n; i ++){
scanf("%lld",&a[i]);
add(i , a[i]);
}
int op,l,r,pre = 0;
while(m--){
scanf("%d",&op);
if(op == 0){
scanf("%d%d",&l,&r);
l = (l ^ pre) % n + 1;
r = (r ^ pre) % n + 1;
if(l > r) swap(l , r);
int ans = 0;
for(int i = 30; i >= 0; i --){
if(pos[r][i] >= l && (ans ^ val[r][i]) > ans){ //在区间内,并且答案更优
ans ^= val[r][i];
}
}
printf ("%d\n",ans);
pre = ans;
}
else{
scanf("%d",&l);
l = l ^ pre;
n++;
add(n , l);
}
}
}
}