【洛谷P3293】美味

题目

题目链接:https://www.luogu.com.cn/problem/P3293
一家餐厅有 \(n\) 道菜,编号 \(1,2,...,n\) ,大家对第 \(i\) 道菜的评价值为 \(a_i\)。有 \(m\) 位顾客,第 \(i\) 位顾客的期望值为 \(b_i\),而他的偏好值为 \(x_i\)。因此,第 \(i\) 位顾客认为第 \(j\) 道菜的美味度为 \(b_i\text{ xor } (a_j+x_i)\)\(\text{xor}\) 表示异或运算。
\(i\) 位顾客希望从这些菜中挑出他认为最美味的菜,即美味值最大的菜,但由于价格等因素,他只能从第 \(l_i\) 道到第 \(r_i\) 道中选择。请你帮助他们找出最美味的菜。
\(n\leq 2\times 10^5;m\leq 10^5;0\leq a_i,b_i,x_i<10^5\)

思路

看到异或和区间询问容易想到可持久化 01 Trie。但是这个 \(+x_i\) 很棘手,Trie 不支持加减一个数。
我们考虑一般的求异或最大值的做法:高位枚举到低位,能选择和 \(x\) 这一位不同的数字就选不同的,否则选择相同的。这样一个贪心策略显然是正确的。
假设我们求完前 \(i-1\) 位时,能与给定的数字 \(b\) 异或起来最大的是 \(ans\),考虑第 \(i\) 位,假设 \(b\) 的这一位是 \(0\),那么我们希望能选出一个数字这一位是 \(1\)。那么这个数字的取值范围应该是 \([ans+2^i,ans+2^{i+1}-1]\)。我们只需要知道序列区间 \([l,r]\) 中是否有数字在这个范围即可。用主席树维护一下就行了。
考虑 \(+x\) 的操作,不过是把区间变为了 \([ans+2^i-x,ans+2^{i+1}-1-x]\)。依然可以主席树查询。
时间复杂度 \(O(m\log n\log a)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=200010,M=100000,LG=18;
int n,m,a[N],rt[N];

struct SegTree
{
	int tot,cnt[N*LG],lc[N*LG],rc[N*LG];
	
	int update(int now,int l,int r,int k)
	{
		int x=++tot;
		cnt[x]=cnt[now]+1; lc[x]=lc[now]; rc[x]=rc[now];
		if (l==r) return x;
		int mid=(l+r)>>1;
		if (k<=mid) lc[x]=update(lc[now],l,mid,k);
			else rc[x]=update(rc[now],mid+1,r,k);
		return x;
	}
	
	int query(int nowl,int nowr,int l,int r,int ql,int qr)
	{
		if (ql>qr) return 0;
		if (ql<=l && qr>=r) return cnt[nowr]-cnt[nowl];
		int mid=(l+r)>>1,res=0;
		if (ql<=mid) res+=query(lc[nowl],lc[nowr],l,mid,ql,qr);
		if (qr>mid) res+=query(rc[nowl],rc[nowr],mid+1,r,ql,qr);
		return res;
	}
}seg;

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		rt[i]=seg.update(rt[i-1],0,M,a[i]);
	}
	while (m--)
	{
		int b,x,l,r,ans=0;
		scanf("%d%d%d%d",&b,&x,&l,&r);
		for (int i=LG-1;i>=0;i--)
		{
			int bit=((b>>i)&1)^1;
			int ql=max(ans+(bit<<i)-x,0),qr=min(ans+(bit<<i)+(1<<i)-1-x,M);
			if (seg.query(rt[l-1],rt[r],0,M,ql,qr)) ans+=(bit<<i);
				else ans+=((bit^1)<<i);
		}
		cout<<(ans^b)<<"\n";
	}
	return 0;
}
posted @ 2021-06-04 18:32  stoorz  阅读(71)  评论(0编辑  收藏  举报