题目链接
题意:给一个(n<=5e5)的序列,有(q<=5e5)次询问,每次询问一段【l,r】区间内选一些数出来能构成的最大xor值是多少。
题解:首先选一些数使得他们的异或值最大,线性基的基本操作。(不会就看这篇大佬的博客)。那么如何处理多个区间查询操作呢。最直接的想法就是用个数据结构来为维护,比如线段树每个节点维护一个区间的线性基,那么两个线性基的合并时间就是log2 c 。线段树的时间复杂度是qlogn。总复杂度为O(qlogn*log2 c)。
有更优的做法。我们可以预处理出以每个位置为右端点的线性基每一位出现的最大位置,用r[i][j]表示。例如r[5][2]表示以5为右端点,线性基第二位出现的最大位置。当然同时要维护其线性基的值。用b[i][j]表示。怎么维护出这两个数组的值呢。
首先从左往右扫,如果没有出现当前位的线性基就直接插入。否则要按照位置作为优先级(后的优先)进行重新构建线性基。意思就是位置靠后的更应该保留原值,位置靠前的做xor在别的位置找线性基。只有满足位置比它大才替换他。这样就能保证r数组是从右往左构造的线性基。(平常我们做的线性基都默认应该保留前面的值作为首次出现的线性基位,这里应该保留后面的)
#include<bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 5e5+5;
int r[maxn][22];//表示令i为右端的区间且线性基第i位有值的最大左端点l在哪
int p[maxn][22];//当前线性基的值
int n,a[maxn],m;
int main() {
scanf("%d",&n);
for(int i=1; i<=n; i++)scanf("%d",&a[i]);
for(int i=1; i<=n; i++) {
int id=i,x=a[i];
for(int j=20; j>=0; j--)r[i][j]=r[i-1][j],p[i][j]=p[i-1][j];
for(int j=20; j>=0; j--) {
if((x>>j)&1) {
if(!p[i][j]) {
p[i][j]=x;
r[i][j]=id;
break;
}
if(r[i][j]<id) {//按位置的优先级排序
swap(x,p[i][j]);
swap(id,r[i][j]);
}
x^=p[i][j];
}
}
}
scanf("%d",&m);
while(m--) {
int L,R;
scanf("%d%d",&L,&R);
int ans=0;
for(int i=20; i>=0; i--) {
if(r[R][i]>=L) {
ans =max(ans,ans^p[R][i]);
}
}
cout<<ans<<endl;
}
return 0;
}