【2019hdu多校第一场B题 HDU - 6579 】Operation(线性基)

There is an integer sequence aa of length n and there are two kinds of operations: 

  • 0 l r: select some numbers from al...ar so that their xor sum is maximum, and print the maximum value.
  • 1 x: append x to the end of the sequence and let n=n+1.

 

Input

There are multiple test cases. The first line of input contains an integer T(T≤10), indicating the number of test cases. 
For each test case: 
The first line contains two integers n,m(1≤n≤5×105,1≤m≤5×105), the number of integers initially in the sequence and the number of operations. 
The second line contains n integers a1,a2,...,an(0≤ai<230) , denoting the initial sequence. 
Each of the next mm lines contains one of the operations given above. 
It's guaranteed that ∑n≤106,∑m≤106,0≤x<230. 
And operations will be encrypted. You need to decode the operations as follows, where lastans denotes the answer to the last type 0 operation and is initially zero:
For every type 0 operation, let l=(l xor lastans)mod n + 1, r=(r xor lastans)mod n + 1, and then swap(l, r) if l>r. 
For every type 1 operation, let x=x xor lastans.

Output

For each type 0 operation, please output the maximum xor sum in a single line.

Sample Input

1
3 3
0 1 2
0 1 1
1 3
0 3 4

Sample Output

1
3

题意:

给n个数,m个询问,每次询问有两种0 l r,代表查询(l^ans)%n+1到(r^ans)%n+1区间内这些数中选一部分异或的最大值(放在ans中),1 x代表在序列末尾加入一个数x^ans。

思路:

当时在比赛时想过线性基,但是感觉暴力的话时间复杂度比较高。结束后跟同学讨论,得知有另一种构造线性基的方式(参考 51-nod 1577)。构造思路,对于每一个数构造一个线性基,这个线性基是以他为结尾,选他之前的并且最靠近他的元素构造的。构造方式是,从左向右遍历数组,把第一个元素加入第一个线性基中,然后之后的线性基先继承前一个线性基,然后再往其中加入当前位置的元素。不过这里加元素和普通的线性基有些不同,这里需要维护每一位的加入的元素的位置即代码中的idx数组。加入新元素时,如果当前位置没有元素,则直接加入,如果有元素,并且这个元素加入时间比当前元素早,就将d数组中的元素和当前要继续插入的元素交换,顺便交换加入时间。然后继续把这个元素看看能不能在后面的位置插入进去。

查找最大值和之前的查找方式差不多,只不过需要先固定右端点,这样就保证选的元素都是在右端点左边的,然后贪心选的元素的同时,需要看看这个元素是不是在左区间的右边即可。加入新的元素时需要为其构建一个新的线性基,构造方式同上。

这里需要注意对线性基数组的初始化,因为T组样例,所以只需要将lb[0]初始化,后面的是在继承前面的基础上,在加元素的。并且这里读入要从1~n读,因为后面询问的l,r是1~n的。因为是在原数组的结尾加元素,所以构造线性基是从左向右构造以某个点为结尾的线性基(否则也可以反方向构造,51nod的那道题两个方向都可以)。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 600000;
int a[maxn];
struct L_B{
	int d[32],idx[32],r;
	L_B(){
		memset(d,0,sizeof(d));
	}
	void clear(){
		memset(d,0,sizeof(d));
	}
	bool insert(int x,int id){
		for(int i=30;i>=0;i--){
			if(x&(1<<i)){
				if(d[i]){
					if(id>idx[i]){
						swap(idx[i],id);
						swap(d[i],x);
					}
					x^=d[i];
				}
				else{
					d[i]=x;
					idx[i]=id;
					r++;
					break;
				}
			}
		}
		return x>0;
	}
	int query_max(int l){
		int ret=0;
		for(int i=30;i>=0;i--){
			if(idx[i]>=l&&(ret^d[i])>ret)
			ret^=d[i];
		}
		return ret;
	}
}lb[maxn];
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++) 
		scanf("%d",&a[i]);
		lb[0].clear();
		for(int i=1;i<=n;i++){
			lb[i]=lb[i-1];
			lb[i].insert(a[i],i);
		}
		int ans=0;
		while(m--){
			int op;
			scanf("%d",&op);
			if(op==0){
				int l,r;
				scanf("%d%d",&l,&r);
				l=(l^ans)%n+1;
				r=(r^ans)%n+1;
				if(l>r) swap(l,r); 
				ans=lb[r].query_max(l);
				printf("%d\n",ans);
			}
			else{
				int tmp;
				scanf("%d",&tmp);
				tmp^=ans;
				n++;
				lb[n]=lb[n-1];
				lb[n].insert(tmp,n);
			}
		}
	}		
	return 0;
}

关于线性基构造方式证明可以参考这个博客:参考博客

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值