题意翻译
给定(n ( 1 leq n leq 500\,000 )) 和(a_{idots n}( 0 leq a_i leq 10^6))
有(q( 1 leq q leq 500\,000))个询问:
给定(l,r ( 1 leq l_i leq r_i leq n))
求在(a_{ldots r})
中选取任意个,使得他们的异或和最大。
前缀线性基的模板题目。
前缀线性基,就是给定数列(a[]),求出(d[i][62])表示数列中前(i)个数的线性基。
很明显,(d[i][62])就相当于在(d[i-1][62])中再插入(a[i]),其余的相同。
这里面,(d[i][j])是由第一个产生(j)位的数产生
我们要求(l,r)间的异或最大值,我们就应当去掉前(l-1)个对结果产生的影响。
这时,(d[i][j])中的(j)可能会对(l,r)产生影响。但是产生它的数在(l)之前,所以要消除它的影响。但是在(l,r)之前可能还有数可以产生第(j)位,但是由于已经产生就不再起作用。这时我们发现我们应当记录最后产生第(j)们的数的位置,以此再判断是否可以用它来进行异或。
所以我们要记录(pos[i][j]),表示前(i)个数的线性基,第(j)位可以由最靠后的第几个数产生。
(color{red}{代码中比较难理解的就是两个swap();可以这样理解:插入x时,如果发现对应的位已经存在,而且产生它的数的位置比当前位置更靠前,那么两个swap()的作用就是把应当的数替换原来的数,并重新插入原来的数。})
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
int n,q;
int sz[maxn];
int d[maxn][32],pos[maxn][32];
int cnt;
void insert(int x)
{
cnt++;
for(int i=0;i<32;++i)
{
d[cnt][i]=d[cnt-1][i];
pos[cnt][i]=pos[cnt-1][i];
}
int p=cnt;
for(int i=31;i>=0;--i)
if(x&(1<<i))
{
if(d[cnt][i])//如果第i个数已经存在
{
if(pos[cnt][i]<p)//且产生它的数比当前位置更靠前
{//用当前x替代原来的数,当然也更新位置
swap(d[cnt][i],x);
swap(pos[cnt][i],p);
}
x^=d[cnt][i];//重新插入
}
else
{
d[cnt][i]=x;
pos[cnt][i]=p;
break;
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",sz+i);
insert(sz[i]);
}
scanf("%d",&q);
int l,r;
while(q--)
{
scanf("%d%d",&l,&r);
int ans=0;
for(int i=31;i>=0;--i)
{
if(pos[r][i]<l)continue;
if((ans^d[r][i])>ans)ans=ans^d[r][i];
}
printf("%d
",ans);
}
return 0;
}