2017ICPC西安现场赛 XOR(线段树合并线性基)

题目链接:https://nanti.jisuanke.com/t/A1607

题目大意 :给你长度为n的序列,以及一个数k,有q次询问,每一次询问给你一个区间[L,R],然后你可以在区间内取任意多个数(每个数只能去一次),异或这些数得到一个值val,使得k|val的最值大,输出这个最大值

分析:或(|)操作,我们知道对应二进制为只要有一个1,结果就为1,所以我们让k|val最大,必须把k的高位0尽可能变为1,那么我们对k的二进制位取反得到一个数x,也变成了我们尽可能保留x的高位1,所以我们把原序列的所有数字对x&操作,也即a[1~n]&x,得到一个新的序列,对于一个询问区间[L,R],我们只需要求出这个区间的数异或得到的最大值即可,这是我们就可以使用线性基来O(n)求得异或最大值,换言之我们还需要维护新序列的线性基,所以我们可以通过线段树合并线性基的操作来快速维护区间的线性基
时间复杂度O(nlogn),因为合并线性基最多只有32位,所以可以忽略不计.

如果没有学过线性基的可以看下面视频
视频链接1:https://www.bilibili.com/video/BV1qK4y1p7p9?from=search&seid=6100927719987938288
视频链接2:https://www.bilibili.com/video/BV1ct411c7EP?from=search&seid=13154213484053366422

AC代码:

/*线段树合并线性基区间操作
线性基用于求一个集合取任意个数合并得到的最大值或求第K小值*/
#include<bits/stdc++.h>
#define ls dep<<1
#define rs dep<<1|1 
using namespace std;
const int Maxn = 1e4+10;
int n,q,k;
struct node{
    
    
	int mx;
	int d[31];
	void init(){
    
    
		memset(d,0,sizeof(d));
		return ;
	}
	/*结构体构造函数,结构体被创建时调用*/
	node(){
    
    
		memset(d,0,sizeof(d));
		mx = 30;
		return ;
	}
	bool add(int x){
    
    
		for(int i=30;i>=0;i--){
    
    
			if(x&(1<<i)){
    
    //x的第i+1位有1 
				if(d[i]){
    
    //线性基集合中有最高位1为第i+1的线性基 
					x^=d[i];
				}
				else{
    
    //线性基集合中没有最高位1为第i+1的线性基
					d[i]=x;
					return true;//线性基插入成功 
				}
			}
		}
		return false;//线性基插入失败 
	}	
	int get_max(){
    
    
		int ans = 0;
		for(int i=mx;i>=0;i--){
    
    
			if((ans^d[i])>ans) ans^=d[i];
		}
		return ans;
	}
	/*重载操作符,和线性基操作*/
	node operator+(const node&a){
    
    
		node ret = *this;
		for(int i=mx;i>=0;i--){
    
    
			if(a.d[i]) ret.add(a.d[i]);
		}
		return ret;
	}
}base[Maxn<<2];
int a[Maxn];
void pushup(int dep){
    
    
	base[dep]  = base[ls]+base[rs];
	return ;
}
void build(int l,int r,int dep){
    
    
	base[dep].init();
	if(l==r){
    
    
		base[dep].add(a[l]);
		return ;
	}
	int mid = l+r>>1;
	build(l,mid,ls);
	build(mid+1,r,rs);
	pushup(dep);
	return ;
}
/*查询对应区间的线性基线段树节点*/
node query(int l,int r,int ql,int qr,int dep){
    
    
		if(ql<=l&&r<=qr){
    
    
			return base[dep];
		}
		int mid = l+r>>1;
		if(mid>=qr) return query(l,mid,ql,qr,ls);
		else if(mid<ql) return query(mid+1,r,ql,qr,rs);
		else return query(l,mid,ql,qr,ls)+query(mid+1,r,ql,qr,rs);	
}
int main(){
    
    
	ios::sync_with_stdio(false);
	int t;
	cin>>t;
	while(t--){
    
    
		cin>>n>>q>>k;
		int p=0;
		for(int i=0;i<=30;i++){
    
    
			if(k&(1<<i)) continue;
			else p|=(1<<i); 
		}
		for(int i=1;i<=n;i++){
    
    
			cin>>a[i];
		    a[i]&=p;
		}
	    build(1,n,1);
	    while(q--){
    
    
	    	int l, r;
	    	cin>>l>>r;
	    	node ans = query(1,n,l,r,1);
	    	int anss=ans.get_max();//获取线性基异或得到的最大值 
	    	anss|=k;
	    	cout<<anss<<'\n';
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/TheWayForDream/article/details/118972529