洛谷P3293美味

Posted stoorz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷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;
}

以上是关于洛谷P3293美味的主要内容,如果未能解决你的问题,请参考以下文章

P3293 [SCOI2016]美味

P3293 [SCOI2016]美味

luogu P3293 [SCOI2016]美味

P3293 [SCOI2016]美味 主席树+最大异或路径

洛谷 P2089 烤鸡

洛谷——背包型dp