【51-nod】1577 异或凑数(特殊的线性基构造方法)

从左到右一共n个数,数字下标从1到n编号。

一共m次询问,每次询问是否能从第L个到第R个数中(包括第L个和第R个数)选出一些数使得他们异或为K。

数据量比较大。

输入请用挂

int read(){
	int ans=0;
	char last=' ',ch=getchar();
	
	while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();

	return ans;
}

输出请用puts


 收起

输入

单组测试数据。
第一行一个整数n(0<n<=500,000)。
第二行n个整数,0<每个数<2^30。
第三行一个数m,表示询问次数(0<m<=500,000)。
接下来m行每行三个数,L,R,K(0<L<=R<=n,0<K<2^30)。

输出

M行,每行为YES或NO

输入样例

5
1 1 2 4 6
3
1 2 1
2 4 8
3 5 7

输出样例

YES
NO
NO

题解:(参考博客1参考博客2参考博客3

实现方式是对每一个数都维护一个线性基,这里我的代码,是以左端点为起点往后选最近的点构成的线性基。从右往左遍历,让先继承前一个线性基,然后加入新的元素。查询时,就是需要判断能不能构成k,以及构成k的所有元素是不是都小于r。

以下部分复制于参考博客:

这个问题集中在于怎么快速提取一个区间的线性基。一个线性基里面的元素可以用线性基外的元素替换的。只要保证还能表示出原来的线性空间,那么一定可以替换。所以,我们给每个点维护一个线性基。

 

#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 500010;
int a[maxn];
struct L_B{
	int d[32],idx[32],r;
	L_B(){
		memset(d,0,sizeof(d));
		r=0;
	}
	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;
	}
	bool check(int r,int x){
		int minn=0;
		for(int i=30;i>=0;i--){
			if(x&(1<<i)){
				if(!d[i]) return false;
				minn=max(minn,idx[i]);
				x^=d[i];
			}
		}
		if(x||minn>r) return false;
		else return true;
	} 
}lb[maxn];
int read(){
	int ans=0;
	char last=' ',ch=getchar();
	while(ch>='0'&&ch<='9')
	ans=ans*10+ch-'0',ch=getchar();
	return ans;
}
int main(){
	int n=read();
	for(int i=1;i<=n;i++)//以后要给l,r所以这里必须从1开始到n 
	a[i]=read();
	lb[n].insert(a[n],n);
	for(int i=n-1;i>=0;i--){
		lb[i]=lb[i+1];
		lb[i].insert(a[i],i);
	}
	int m=read();
	while(m--){
		int l,r,k;
		l=read();r=read();k=read();
		if(lb[l].check(r,k))puts("YES");
		else puts("NO");
	}
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值