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';
	}
}
posted @ 2024-01-25 22:07  Ishar-zdl  阅读(17)  评论(0)    收藏  举报