cf1100f
题意翻译
给定\(n ( 1 \leq n \leq 500\,000 )\) 和\(a_{i\dots 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_{l\dots 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\n",ans);
}
return 0;
}