题目描述
给定一个长度为 n 的数列 A1,A2,⋅⋅⋅,An 和一个非负整数 x,给定 m 次查询,每次询问能否从某个区间 [l,r] 中选择两个数使得他们的异或等于 x。
输入输出格式及数据范围
输入输出样例
思路
①对于两个数的异或值为x,可以化简为,在区间L和R之间,寻找一个数b,使得b = a ^ x
,此处我们不妨规定b < a
,指的是b的下标小于a。
②但是本题的数据范围是1e5,如果使用①的方法思考,那么时间复杂度为O(n2),会超时,我们需要将时间复杂度控制在O(nlogn)之内,因此需要一种优化方法。
③对于区间L~R
,我们不妨令①中的a为ai,这样我们总是需要寻找一个在ai左侧的b,b的下标需要大于等于L,如果存在这样的b,就可以说区间中存在满足题意的数对。令f[i]
为b的下标,即如果f[i] ≥ L
,则可以输出“yes”,否则输出“no”。
④但是按照③中的思路维护f[i]
,是在区间L和R中进行维护,L和R的自由度之和为O(n2),仍然不能满足题意,因此还需要进一步的优化。值得注意的是,由于我们认为约定b < a
(b的下标小于a,即b在a的左侧寻找),因此对于ai,如果a的下标i<L
,那么f[i]
自然会小于L,f存的是b的下标,b的位置一定在a的左侧,更不可能处于区间L和R之中。因此可以优化为在1~R
维护数组f,对于ai,由于其左侧可能有多个f[j]
满足大于L(但是这个f[j]可能不是由ai贡献的,而是位置比ai靠左的另一个数贡献的),因此可以另外维护数组g,来存储位置i左侧做靠右的满足题意条件的下标,即最大的f[j]
。
⑤综上所述,如果g[R] >= L
,即R的左侧存在一个ai,使得f[ai] ≥ L,则输出“yes”,否则输出“no”。
代码
- 注:数组last记录的是输入的数字a的下标。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 5, maxm = (1<<20) + 5;
int last[maxn] = {0}, g[maxm] = {0};
int n, m, x;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m>>x;
for(int i=1;i<=n;i++){
int a;
cin>>a;
g[i] = max(g[i-1], last[a^x]);
last[a] = i;
}
for(int i=1;i<=m;i++){
int l, r;
cin>>l>>r;
if(g[r] >= l) cout<<"yes"<<endl;
else cout<<"no"<<endl;
}
return 0;
}