选数异或(第十三届蓝桥杯赛题)

给定一个长度为\textit{n}的数列A_{1},A_{2},\cdots ,A_{n} 和一个非负整数x,给定m次查询每次询问能否从某个区间[l,r]中选择两个数使得他们的异或等于 x

输入格式

输入的第一行包含三个整数 n,m,x

第二行包含n个整数A_{1},A_{2},\cdots ,A_{n}

接下来 m行,每行包含两个整数 l_{i},r_{i}表示询问区间[l_{i},r_{i}]

输出格式

对于每个询问,如果该区间内存在两个数的异或为x 则输出 yes,否则输出 no

数据范围

对于 的评测用例,1 \leqslant n,m\leq 100
对于的评测用例,1\leqslant n,m\leqslant 1000
对于所有评测用例,1\leqslant n,m\leqslant 100000,0\leqslant x< 2^{20},1\leqslant l_{i}\leqslant r_{i}\leqslant n,0\leqslant A_{i}< 2^{^{20}}

输入样例

4 4 1
1 2 3 4
1 4
1 2
2 3
3 3

 输出样例

yes
no
yes
no

重要异或运算性质

归零律:a\oplus a=0

结合律: a\oplus b\oplus c=a\oplus \left(b\oplus c \right ) = \left(a\oplus b \right ) \oplus c

自反:a \oplus b = b\oplus c

因此,a \oplus b = x \Leftrightarrow a \oplus b\oplus x = 0 \Leftrightarrow a \oplus x = b

因此对于一个数a,其对应的数可以直接计算得出,即为a \oplus x

动态规划

将两个配对的数a,b简称为数对

定义dp[i]为区间[1.i]中所有数对中的最大下界

当查询区间[l,r]右边界为r时,至少包含一个数对时的左边界最大值,所以如果l小于左边界最大值(dp[r]),[l,r]内至少有一个数对

代码实现

使用哈希表last[i]存储记录值i最后一次出现的位置下标,状态转移方程:

dp[i] = max \left(dp[i], last[a_{i} \oplus x]\right )

代码

#include
#include
#include
using namespace std;
const int N = 100010;
int n,m,l,r,x;
int dp[N];
unordered_map last;
inline int max(int a,int b){
    return a > b ? a : b;
}
int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    //ios_base::sync_with_stdio(0);
    //cin.tie(0);
    cin >> n >> m >> x;
    for(int i = 1;i <= n;i++){
        int a;
        cin >> a;
        last[a] = i;
        dp[i] = max(dp[i-1],last[a ^ x]);
    }
    while(m--){
        cin >> l >> r;
        if(dp[r] >= l) cout << "yes" <

你可能感兴趣的:(基础算法,蓝桥杯,职场和发展)