题目 2665:
蓝桥杯2022年第十三届省赛真题-选数异或
时间限制: 3s 内存限制: 320MB
题目描述
给定一个长度为 n 的数列 A1, A2, · · · , An 和一个非负整数 x,给定 m 次查询, 每次询问能否从某个区间 [l,r] 中选择两个数使得他们的异或等于 x 。
输入格式
输入的第一行包含三个整数 n, m, x 。
第二行包含 n 个整数 A1, A2, · · · , An 。
接下来 m 行,每行包含两个整数 li ,ri 表示询问区间 [li ,ri ] 。
输出格式
对于每个询问, 如果该区间内存在两个数的异或为 x 则输出 yes, 否则输出 no。
样例输入
复制
4 4 1 1 2 3 4 1 4 1 2 2 3 3 3
样例输出
复制
yes no yes no
提示
显然整个数列中只有 2, 3 的异或为 1。
对于 20% 的评测用例,1 ≤ n, m ≤ 100;
对于 40% 的评测用例,1 ≤ n, m ≤ 1000;
对于所有评测用例,1 ≤ n, m ≤ 100000 ,0 ≤ x < 220 ,1 ≤ li ≤ ri ≤ n , 0 ≤ Ai < 220。
解题思路:
我们要把其转化为一个求范围的思路。我们知道a⊕b=x,则a⊕x=b,将其转化为求解b,然而将求解找到b又转化为找到了比 l 数字位置大的就行了。
假设有 a1 a2 a3 a4 a5几个数,其范围l=3,r=5,异或值为 x ,我们就可以求解a5⊕x=a4,a5⊕x=a3,a5⊕x=a2,只要存储离r最大的一个边界值就行,比如是4,然后我们看存储数字的数组f[r]>=l,就说明其是yes。
使用一个数组f[i]记录左边的、与第i个数配对的数的下标为f[i],此时,f[i]<i恒成立问题转化为:在L<=i<=R中,找到一个i满足f[i]>=L现在,我们固定i为R,问题再次转化:在区间[1,R)中找到一个j满足f[j]>=L只要我们找到这样的j,那么就可以知道,在区间[L,R]中至少存在一对数配对虽然刚才说固定i为R,但其实i不是真的等于R,实际做法是:对于第i个位置,我们求出区间[1,i)内最大的f[i],假设用g数组来存当询问到区间[L,R]时,我们判断等式f[R]>=L是否成立,若成立说明存在一个下标f[i]>=L,且i<=R,因为我们在求g[R]的时候并没有用到下标大于R的值此时就说明有配对。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int a[N],f[N],t[N];
int main(){
int n,m,x;
cin>>n>>m>>x;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){ //求一个坐标是他离r最近,且它能异或求解到另外一个数
f[i]=max(f[i-1],t[a[i]^x]); //求左前缀最大值,也就是最近的一个坐标
t[a[i]]=i; //用于标记该值放在数组那个位置
}
while(m--){
int l,r;
scanf("%d%d",&l,&r);
if(f[r]>=l) //如果这个数大于l就是可以异或
cout<<"yes\n";
else
cout<<"no\n";
}
return 0;
}
7272





