题解:洛谷 P3293([SCOI2016] 美味)

Link

1. Description

给定一个长为 \(n\) 的序列 \(a\)\(q\) 个询问,每个询问形如 \((b,x,l,r)\),表示求 \(\max\limits_{i=l}^r[b\otimes (a_i+x)]\),其中 \(\otimes\) 表示异或 。

2. Solution

这道题的数据水到暴力吸臭氧都能过,还能怎么样呢?赞美吧!

看到最大异或和,我们直觉就是字典树,那么这道题能不能用字典树写呢?显然不可以,因为我们对原序列做了一个 \(+x\) 的操作,而字典树显然不好维护一个区间加的操作,所以这道题并不可以用字典树写。

我们考虑在字典树上求最大异或和,实际上就是一个贪心的过程,在高位尽可能地向不一样的位置跳,这个时候我们也可以借鉴这种思想,尽可能在高位找到一个不一样的位置,也就说,我们从高到低枚举二进制下的每一位,当 \(b_i\) 的这一位是 \(0\) 时,我们去找这一位上是 \(1\)\(a_i+x\),反之,我们去找这一位上是 \(0\)\(a_i+x\)

考虑我们在前 \(i-1\) 位上贪心找到的数字为 \(ans\),如果我们需要找第 \(i\) 位为 \(1\) 的数字,也就是找 \([l,r]\) 这个区间内有没有 \(a_i+x\in[ans+2^i,ans+2^{i+1}-1]\),反之,就是找有没有 \(a_i+x\in [ans,ans+2^i-1]\),略作变形,就可以得到,分别找 \(a_i\in [ans+2^i-x,ans+2^{i+1}-1-x]\)\(a_i\in [ans-x,ans+2^i-1-x]\)

此时问题就被我们转换为在线求一个区间内是不是有大小位于一个区间内的数的问题,可以直接使用权值主席树来求解。

3. Code

/*by qwer6*/
/*略去快读快写与缺省源*/
const int N=2e5+5,Mx=2e5;
int n,m;
int rt[N];
struct Segment_tree{
	int num;
	int c[N*30],ls[N*30],rs[N*30];
	#define mid (l+r>>1)
	int New(){
		num++;
		c[num]=ls[num]=rs[num]=0;
		return num;
	}
	int copy(int p){
		int q=New();
		c[q]=c[p],ls[q]=ls[p],rs[q]=rs[p];
		return q;
	}
	void pushup(int p){
		c[p]=c[ls[p]]+c[rs[p]];
	}
	int build(int l,int r){
		int p=New();
		if(l==r)return p;
		ls[p]=build(l,mid),rs[p]=build(mid+1,r);
		return p;
	}
	int change(int p,int l,int r,int x,int v){
		int q=copy(p);
		if(l==r){
			c[q]+=v;
			return q;
		}
		if(mid>=x)ls[q]=change(ls[p],l,mid,x,v);
		else rs[q]=change(rs[p],mid+1,r,x,v);
		pushup(q);
		return q;
	}
	int query(int p,int q,int l,int r,int L,int R){
		if(L<=l&&r<=R)return c[p]-c[q];
		int res=0;
		if(mid>=L)res+=query(ls[p],ls[q],l,mid,L,R);
		if(mid<R)res+=query(rs[p],rs[q],mid+1,r,L,R);
		return res;
	}
	int query(int p,int q,int L,int R){
		tomin(R,Mx),tomin(L,Mx+1);
		tomax(R,0),tomax(L,1);
		if(L>R)return 0;
		return query(p,q,1,Mx,L,R);
	}
	#undef mid
}Set;
signed main(){
	read(n),read(m);
	rt[0]=Set.build(1,Mx);
	for(int i=1,x;i<=n;i++){
		read(x);
		rt[i]=Set.change(rt[i-1],1,Mx,x,1);
	}
	for(int i=1,b,x,l,r,res;i<=m;i++){
		read(b),read(x),read(l),read(r);
		res=0;
		for(int j=18,tmp;j>=0;j--){
			if(b&1<<j){
				tmp=Set.query(rt[r],rt[l-1],res-x,res-x+(1<<j)-1);
				if(!tmp)res+=1<<j;
			}else{
				tmp=Set.query(rt[r],rt[l-1],res-x+(1<<j),res-x+(1<<j+1)-1);
				if(tmp)res+=1<<j;
			}
		}
		write(res^b),Nxt;
	}
}
posted @ 2025-05-17 21:22  陈牧九  阅读(10)  评论(0)    收藏  举报