[UVALive - 8512]XOR(区间线性基板子)

题面
大意
给一个长为 n n n的数组 a [ ] a[] a[] 1 < = a i < = n 1<=a_i<=n 1<=ai<=n
给出一个 Q Q Q, K K K
然后有 Q Q Q个询问,每个询问给出区间 [ L , R ] [L,R] [L,R],要求回答 K K K a L a_L aL~ a R a_R aR的一个子集(可以是空集)的异或值的最大值。

思路
网上多数是线段树+线性基的写法,我来放一个学长教的不需要线段树的写法。
区间线性基

  • 首先,将原数组中的数从左到右插入到线性基中,每插入一个数都维护一个新的线性基,用b[MAXN][62]来保存
    如插入原数组第i位时得到了[1,i]之间的线性基b[i][62],在插入第i+1位时首先复制第i位的线性基,然后将a[i+1]加入到b[i+1][62]中就直接得到了[1,i+1]之间的线性基
    这样每次询问[l,r]之间的线性基时可以先直接得到[1,r]之间的线性基。
  • 首先,我们在将数字插入到线性基时同时维护一个pos[MAXN][62]
    数组,pos[i][j]保存插入a[i]后[1,i]之间的线性基第j位的数字是由原数组中哪
    个数字得到的。
  • 其次,在插入时我们要保证原数组中靠右插入的数字尽可能在线性基
    的高位出现。
  • 这样,我们在访问[l,r]之间的线性基时,首先得到[1,r]之间的线性基b[r][62]
    然后从高位向低位遍历b[r],如果pos[r][i] >= l,则将b[r][i]加入到[l,r]之间的线性基中,这样便可以得到[l,r]之间的线性基
    因为首先如果pos[r][i] < l,那么b[r][i]是由原数组l位置之前的数字插入得到的,肯定不属于[l,r]对应的线性基,如此原数组l之前的数直接去除掉了,而[l,r]之间的数在插入到线性基中时不会受l之前的数影响(原数组中靠右的数字的保留在线性基的高位,[l,r]之间的数字在插入时如果需要与线性基中的数进行异或操作也只会与[l,r]之间的数字异或发生改变,而l之前的数字因为pos较小会被替换),从而[l,r]之间的数字都被完整的保留在线性基中。

关键操作
关于如何使靠右的数字尽量保留在高位。
这是插入操作:

	void insert(ll now,ll t)//插入
	{
		for(int i=NUM;i>=0;--i)
		{
			if(now&(1ll<<i))
			if(this->b[i])
			{
				push_down(this->b[i]^now,i-1,pos[i]);
				//考虑新插入的必然是最右的,故必然可以把冲突位置上的数【顶掉】
				this->b[i]=now;pos[i]=t;return;
			}
			else 
			{
				this->b[i]=now;pos[i]=t;return;
			}
		}
		return;
	}

这是push_down部分:

void push_down(ll now,ll pt,ll o_pos)
	{
		for(ll i=pt;i>=0;i--)
		{
			if(now&(1ll<<i))
			
			if(this->b[i])
			{
				if(this->pos[i]<o_pos)
				//因为不是最新插入的数,故这里需要判断当前下推的数是否比【冲突】的数靠右
				//顺便吐槽下数据真心弱……之前没写这个判断误打误撞也过了
				{
					//在右边则下推当前位置的数
					push_down(this->b[i]^now,i-1,pos[i]);
					//递归处理,如果仍然冲突就先把当前冲突的再次下推
					this->b[i]=now;pos[i]=o_pos;return;
				}else now^=this->b[i];
				//如果在冲突的数左边则不下推
			}else 
			{
				this->b[i]=now;this->pos[i]=o_pos;return;
			}
		}
	}

AC代码
顺便放个板子

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
typedef struct L_B
{
#define NUM 30	
	private:
	ll b[NUM+5],cnt,pos[NUM+5];
	public:
	void push_down(ll now,ll pt,ll o_pos)
	{
		for(ll i=pt;i>=0;i--)
		{
			if(now&(1ll<<i))
			
			if(this->b[i])
			{
				if(this->pos[i]<o_pos)
				{
					push_down(this->b[i]^now,i-1,pos[i]);
					this->b[i]=now;pos[i]=o_pos;return;
				}else now^=this->b[i];
			}else 
			{
				this->b[i]=now;this->pos[i]=o_pos;return;
			}
		}
	}
	void insert(ll now,ll t)//插入
	{
		for(int i=NUM;i>=0;--i)
		{
			if(now&(1ll<<i))
			if(this->b[i])
			{
				push_down(this->b[i]^now,i-1,pos[i]);
				this->b[i]=now;pos[i]=t;return;
			}
			else 
			{
				this->b[i]=now;pos[i]=t;return;
			}
		}
		return;
	}
	void clear()
	{
		for(ll i=0;i<NUM;i++)this->b[i]=0;
	}
	void rebuild()//重构 
	{
		for(int i=1;i<=NUM;i++)
		if(this->b[i])
		{
			for(int j=0;j<i;j++)
			{
				if(this->b[i]&(1ll<<j))
				this->b[i]^=this->b[j];
			}
		}
		return;
	}
	ll get_max(ll now,ll l)//取最大值 
	{
		ll ret=now;	
		for(int i=NUM;i+1;--i)
		{
			if(pos[i]>=l)
			if((ret^this->b[i])>ret)ret^=this->b[i];
		}
		return ret;
	}
#undef NUM
} L_B;
ll n,q,qu,t,a[10005],k,l,r;
L_B B[10005];
ll find(ll l,ll r,ll k)
{
	L_B temp=B[r];
	return temp.get_max(k,l);
}
int main()
{
	scanf("%lld",&t);
	while(t--)
	{
		memset(B+1,0,n*sizeof(L_B));
		scanf("%lld%lld%lld",&n,&q,&k);
		for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);
		B[1].insert(a[1],1);
		for(ll i=2;i<=n;i++)
		{
			B[i]=B[i-1];B[i].insert(a[i],i);
		}
		for(ll i=1;i<=q;i++)
		{
			scanf("%lld%lld",&l,&r);
			printf("%lld\n",find(l,r,k));
		}
	}
	return 0;
}

你可能感兴趣的:(数据结构-线性基)