P3293 [SCOI2016] 美味 题解
题目
我们首先考虑对 \(b_i\bigoplus \left(a_i+x_i\right)\) 进行处理,可以设它为 \(ans\)。
每次查询我们可以将 \(b_i\) 进行二进制拆分,对于 \(b_i\) 在二进制下的每一位,我们自然是想要 \(ans\) 的这一位与它相反,这样才能使最后的答案最大。
我们依次对 \(b_i\) 的二进制每一位进行探讨,如果 \(b_i\) 的从右往左数第 \(k\) 位是 \(0\),那我们想要 \(ans\) 的这一位为 \(1\),\(ans\) 范围就是 \(\left[ans+(1\ll (k-1)),ans+(1\ll k)-1\right]\),反之,\(ans\) 的范围就是 \(\left[ans,ans+(1\ll (k-1))-1\right]\)。
对于每次询问我们依次按每一位查找范围,然后在 \(l\sim r\) 的范围内查找有没有符合这个这个范围的数。将 \(ans\) 更新即可。
代码如下:
#include<bits/stdc++.h>
inline int read(){
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
const int N=2e5+10;
struct Tree{
int siz,ls,rs;
}t[N<<5|1];
int n,m,root[N],cnt;
inline void update(int p){t[p].siz=t[t[p].ls].siz+t[t[p].rs].siz;}
inline void change(int last,int p,int a,int l,int r){
if(l==r){t[p].siz=t[last].siz+1;return;}
int mid=(l+r)>>1;
t[p]=t[last];
if(a<=mid)change(t[last].ls,t[p].ls=++cnt,a,l,mid);
else change(t[last].rs,t[p].rs=++cnt,a,mid+1,r);
update(p);
}
inline bool query(int last,int p,int x,int y,int l,int r){
if(l>=x&&r<=y)return t[p].siz-t[last].siz;
int mid=(l+r)>>1;
bool pd=0;
if(x<=mid)pd|=query(t[last].ls,t[p].ls,x,y,l,mid);
if(y>mid)pd|=query(t[last].rs,t[p].rs,x,y,mid+1,r);
return pd;
}
int main(){
// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
n=read();m=read();
for(int i=1;i<=n;++i)change(root[i-1],root[i]=++cnt,read(),1,N);
for(int i=1;i<=m;++i){
int b=read(),x=read(),l=read(),r=read(),ans=0,lf,rf;
for(int o=17;o>=0;--o){
bool s=(1<<o)&((b>>o)<<o);int zc;
if(s)lf=ans,rf=ans+(1<<o)-1,zc=1;
else lf=ans+(1<<o),rf=ans+(1<<(o+1))-1,zc=0;
if(rf<x)break;
if(query(root[l-1],root[r],std::max(lf-x,0),std::min(N,rf-x),1,N))ans=lf;
else ans+=zc<<o;
}
std::cout<<(ans^b)<<'\n';
}
}