美味(food) | 题解

美味(food)

Outline

n 个数 ai,有 m 个询问,每个询问都会有 b、x、l、r ,用于计算在 l~r 之间的数以 b xor (ai+x)最大值

Input

第一行,两个整数, n , m n, m n,m,表示菜品数和顾客数。
第二行, n n n 个整数, a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an,表示每道菜的评价值。
第三至 m + 2 m + 2 m+2 行,每行 4 4 4 个整数, b , x , l , r b, x, l, r b,x,l,r,表示该位顾客的期望值,偏好值,和可以选择菜品区间。

Output

输出 m m m 行,每行一个整数表示该位顾客选择的最美味的菜的美味值。

Thinking

都提到 位运算 了,那很有可能会与 二进制位 有关,发现尽量让答案的 每一二进制位上的数为1 会最大。那么就 按最高位取下来 ,看看能不能从那区间中找到这一位满足要求(此需 分类 ,若b此位为1,那么就要取0;否则要取1)的数(这里有点不知道怎么说,大概就是在保证前面取了1的位这个数也取,并且当前位位满足要求)。于是便能想到 权值线段树 来优化查询速度。

Code
#include<bits/stdc++.h>
#define maxn 212345
#define mid ((l+r)/2)
using namespace std;

int n,m,a[maxn];
int rt[maxn],tot,mx,ls[maxn*40],rs[maxn*40],v[maxn*40];

void add(int &k,int las,int l,int r,int x){
	if(!k) k=++tot;
	if(l==r){v[k]=v[las]+1; return;}
	if(x<=mid) add(ls[k],ls[las],l,mid,x),rs[k]=rs[las];
	else add(rs[k],rs[las],mid+1,r,x),ls[k]=ls[las];
	v[k]=v[ls[k]]+v[rs[k]];
}

int ask(int k,int las,int l,int r,int L,int R){
	if(R<l || L>r) return 0;
	if(L<=l && r<=R) return v[k]-v[las];
	return ask(ls[k],ls[las],l,mid,L,R)+ask(rs[k],rs[las],mid+1,r,L,R);
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		scanf("%d",&a[i]),mx=max(mx,a[i]);
	int b,x,L,R;
	for(int i=1;i<=n;++i) add(rt[i],rt[i-1],0,mx,a[i]);
	while(m--){
		scanf("%d%d%d%d",&b,&x,&L,&R);
		int ans=0;
		for(int i=20;i>=0;--i){
			int t=(1<<i),f=(b&t);
			if(f && !ask(rt[R],rt[L-1],0,mx,max(ans-x,0),min(ans+t-1-x,mx))) ans+=t;
			else if(!f && ask(rt[R],rt[L-1],0,mx,max(ans+t-x,0),min(ans+t*2-1-x,mx))) ans+=t;
		}
		printf("%d\n",ans^b);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值