cf1100f

题意翻译

给定(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;
}
原文地址:https://www.cnblogs.com/gryzy/p/15110215.html