AcWing 4645. 选数异或
原题
给定一个长度为 n 的数列 A1,A2,⋅⋅⋅,An 1, 2,···, 和一个非负整数 x ,给定 m 次查询,每次询问能否从某个区间 [l,r][ , ] 中选择两个下标不同的数使得他们的异或等于 x 。
输入格式
输入的第一行包含三个整数 n,m,x , , 。
第二行包含 n 个整数 A1,A2,⋅⋅⋅,An 1, 2,···, 。
接下来 m 行,每行包含两个整数 li,ri , 表示询问区间 [li,ri][ , ]。
输出格式
对于每个询问,如果该区间内存在两个数的异或为 x 则输出 yes
,否则输出 no
。
数据范围
对于 20%20% 的评测用例,1≤n,m≤1001≤ , ≤100;
对于 40%40% 的评测用例,1≤n,m≤10001≤ , ≤1000;
对于所有评测用例,1≤n,m≤1000001≤ , ≤100000,0≤x<2200≤ <220,1≤li≤ri≤n1≤ ≤ ≤ ,0≤Ai<2200≤ <220。
输入样例:
4 4 1
1 2 3 4
1 4
1 2
2 3
3 3
输出样例:
yes
no
yes
no
样例解释
显然整个数列中只有 2,32,3 的异或为 11。
难度:中等 |
---|
时/空限制:1s / 256MB |
总通过数:4100 |
总尝试数:8669 |
来源:第十三届蓝桥杯省赛C++A/C/研究生组 |
算法标签 |
题解
#include<iostream>
#include<math.h>
using namespace std;
// 注意+的优先级大于移位的优先级
// 另外为了防止溢出一般设置的时候会多10个
const int LAST=(1<<20)+10,F=100010;
int last[LAST]={0},f[F]={0};
int main()
{
// 输入 +获取last(一个下标是原始数,值是原始下标的数组)
int n,m,x;
cin>>n>>m>>x;
int a,l[m],r[m];
for(int i=1;i<=n;i++)
{
cin>>a;
f[i]=max(last[a^x],f[i-1]);
last[a]=i;
}
// 输入下标边界
for(int i=0;i<m;i++)
{
cin>>l[i];
cin>>r[i];
}
// 输出
for(int i=0;i<m;i++)
{
if(f[r[i]]>=l[i]) printf("yes\n");
else printf("no\n");
}
}
思路
参考闫总的视频
一一一一一一一一一一一一一一一一一一一一一一一一一一一→
↑ ↑ ↑ ↑
L j i R
- 1、看懂题目:l和r指的是下标,(可以看看下面l和r的取值范围理解)
- 2、从r开始往左边遍历,对于遍历到的每个i,找到它左边(且离它最近的)的满足异或条件(A[i]A[j]=x→xA[i]=A[j])的最大的下标j,只要能找到就是yes,如果不能找到满足条件的j则是no
- 3、条件:因为i<r,所以j只需要大于L即可,因此i的范围可以推广至0<i<r,因为小于L部分的i找到的j不可能大于L(j在i的左边)
- 4、所以,程序中,f[i]记录的是从1~i 的最大的j ,只要从下标为1~r的A[i]的异或值A[j]的下标j中最大的j 大于L即输出yes
- 5、last记录的是值=A[…]的最大的从左边开始遍历原始数组到目前最大的下标