从左到右一共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
实现方式是对每一个数都维护一个线性基,这里我的代码,是以左端点为起点往后选最近的点构成的线性基。从右往左遍历,让先继承前一个线性基,然后加入新的元素。查询时,就是需要判断能不能构成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;
}