[SCOI2016]美味

问题描述

一家餐厅有 n 道菜,编号 1…n ,大家对第 i 道菜的评价值为 ai(1≤i≤n)。有 m 位顾客,第 i 位顾客的期望值为 bi,而他的偏好值为 xi 。因此,第 i 位顾客认为第 j 道菜的美味度为 bi XOR (aj+xi),XOR 表示异或运算。第 i 位顾客希望从这些菜中挑出他认为最美味的菜,即美味值最大的菜,但由于价格等因素,他只能从第 li 道到第 ri 道中选择。请你帮助他们找出最美味的菜。

输入格式

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

输出格式

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

样例输入

4 4
1 2 3 4
1 4 1 4
2 3 2 3
3 2 3 3
4 1 2 4

样例输出

9
7
6
7

数据范围

1≤n≤2×10​^5​​,
0≤a​i​​,b​i​​,x​i​​<10^​5​​,
1≤l​i​​≤r​i​​≤n(1≤i≤m),
1≤m≤10^​5​​


题解

简化题意,即给定一个序列a[ ],多次询问,每次给定b 、x、l、r,求b^(a[j]+x)最大值(l<=j<=r)
显然,根据贪心,若最高位异或之后可以为1 ,那么我们肯定让它是 1 ,不管这样做会不会影响之后的几位。
因此,我们用ans记录(a[j]+x)。
假设讨论到第 i 位(第0位开始从右往左数)。
如果b这一位是0,那么我们肯定希望ans这一位是1,那么ans’ 在满足之前条件的情况下的取值范围是?[ans|(1<< i),ans|(1<< (i+1)-1)]。因此,若a序列中有值在[-x+ans|(1<< i),-x+ans|(1<<(i+1)-1)]的数,就可以满足希望。
如果b 这一位是1,那么我们肯定希望ans这一位是0,那么ans’ 在满足之前条件的情况下的取值范围就是[ans,ans|((1<< i)-1)]。因此,若a序列中有值在[-x+ans,-x+ans|((1<< i)-1)]的数,就可以满足希望。
将i 从大到小讨论,最后输出ans^b即可。
具体见代码。


代码

#include <stdio.h>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int Q=200005,maxn=100000;
int ls[Q<<5],rs[Q<<5],cnt[Q<<5],n,tot;
void xiu(int now,int last,int l,int r,int x)
{
    cnt[now]=cnt[last]+1;
    if(l==r)return;
    ls[now]=ls[last];
    rs[now]=rs[last];
    int mid=(l+r)>>1;
    if(x<=mid)ls[now]=++tot,xiu(ls[now],ls[last],l,mid,x);
    else rs[now]=++tot,xiu(rs[now],rs[last],mid+1,r,x);
}
bool get(int nowl,int nowr,int l,int r,int x,int y)
{
    if(x<0)x=0;
    if(y<0||cnt[nowr]-cnt[nowl]<=0)return false;
    if(x<=l&&y>=r)return true;
    int mid=(l+r)>>1;
    if(x<=mid&&get(ls[nowl],ls[nowr],l,mid,x,y))return true;
    if(y>mid&&get(rs[nowl],rs[nowr],mid+1,r,x,y))return true;
    return false;
}
int main()
{
    int i,m,l,r,x,b,ans;
    scanf("%d%d",&n,&m);
    tot=n;
    for(i=1;i<=n;i++)
        scanf("%d",&x),xiu(i,i-1,0,maxn,x);
    while(m--)
    {
        scanf("%d%d%d%d",&b,&x,&l,&r);
        ans=0;
        for(i=17;i>=0;--i)
        {
            if(((b>>i)&1)==0)if(get(l-1,r,0,maxn,ans+(1<<i)-x,ans+(1<<(i+1))-1-x))ans|=(1<<i);
            if((b>>i)&1)if(!get(l-1,r,0,maxn,ans-x,ans-x+(1<<i)-1))ans|=(1<<i);
        }
        printf("%d\n",ans^b);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值