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;
}

posted on 2021-08-06 20:30  gryzy  阅读(32)  评论(0编辑  收藏  举报

导航