
题意
m次询问[l,r]区间内是否有两个数异或为x
分析
当看到 mmm 次查询时,如果每次都去区间里重新找一遍,代价太大。我们需要预处理 。
对于一个固定的右端点 RRR,什么样的左端点 LLL 是合法的?
- 假设我们在位置 iii 有一个数 AiA_iAi。
- 要想凑成异或 xxx,我们需要另一个数 target=Ai⊕xtarget = A_i \oplus xtarget=Ai⊕x。(使用异或的性质)
- 如果 targettargettarget 曾经在位置 jjj (j<ij < ij<i) 出现过,那么区间 [j,i][j, i][j,i] 就是一个合法区间。
- 并且任何包含 [j,i][j, i][j,i] 的区间(即左端点 ≤j\le j≤j,右端点 ≥i\ge i≥i)都是合法的,即可以向外延申。
对每次询问是否存在,将"存在性问题"转化为"贪心问题"
在右端点固定为 rrr 的情况下,为了满足条件,左端点距离右端点最近到哪里?
- 定义 dp[i]dp[i]dp[i] 为:以 iii 为右端点,能够满足"区间内存在两数异或为 xxx"的最靠右的左端点位置。
代码
cpp
void solve() {
int n,m,x;cin>>n>>m>>x;
vector<int>dp(n+1);//dp[i] 表示区间[dp[i],i]之间有两数a^b=x
map<int,int>lstpos;
forr(i,1,n){
int a;
cin>>a;
dp[i]=max(dp[i-1],lstpos[a^x]);// 找最右边的合法左端点
lstpos[a]=i;// 记录这个值出现的最右位置
}
forr(i,1,m){
int l,r;cin>>l>>r;
cout<<(dp[r]>=l?"yes":"no")<<endl;
}
/*
// 二分太麻烦了 直接索引最右左端点就可以
vector<pii>pos;
sort(a.begin()+1,a.end(),[&](pii x,pii y){
return x.fir<y.fir;
});
vector<int>b(n+1);
map<int,int>kv;
forr(i,1,n){
b[i]=a[i].fir;
kv[i]=a[i].sec;
}
forr(i,1,n){
int fpos=lower_bound(b.begin()+i+1,b.end(),b[i]^x)-b.begin();
if(fpos>n||b[fpos]!=(b[i]^x))continue;
if(fpos==i)fpos++;
int bpos=upper_bound(b.begin()+1,b.end(),b[i]^x)-b.begin();
bpos--;
if(b[bpos]!=(b[i]^x))continue;
forr(p,fpos,bpos){
pos.push_back({})
}
}
forr(q,1,m){
int fg=0;
cout<<(fg?"yes":"no")<<endl;
}
*/
}