P3293 [SCOI2016]美味

Description

给出一个长度为n的序列,m询问,每次询问求出[l,r]范围内的每一个数加上x再与b异或能够得到的最大值。


Input

第1行,两个整数,n,m,表示菜品数和顾客数。
第2行,n个整数,a1,a2,…,an,表示每道菜的评价值。
第3至m+2行,每行4个整数,b,x,l,r,表示该位顾客的期望值,偏好值,和可以选择菜品区间。
1 ≤ n ≤ 2 × 1 0 5 , 0 ≤ a i , b i , x i < 1 0 5 , 1 ≤ l i ≤ r i ≤ n ( 1 ≤ i ≤ m ) ; 1 ≤ m ≤ 1 0 5 1≤n≤2×10^5,0≤ai,bi,xi<10^5,1≤li≤ri≤n(1≤i≤m);1≤m≤10^5 1n2×1050ai,bi,xi1051lirin(1im)1m105


Output

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


Solution

我们把b转换成二进制。
假设二进制下是 b k b k − 1 . . . b 0 b_kb_{k-1}...b_0 bkbk1...b0
答案肯定<=100000 有18位(17~0)
我们从高位往低位贪心取,每次判断答案的这一位能不能取1.
假设我们处理到第i+1位,答案和为sum,要处理第i位。
那么新的ans= ∈ \in [ s u m + 2 i , s u m + 2 i + 1 − 1 sum+2^i,sum+2^{i+1}-1 sum+2i,sum+2i+11]
而这个能不能取到可以在权值线段树找。

[l,r]区间限制动态开点建版本差分就好了。


Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node {
    int ls,rs,siz;
} tree[6000000];
int rt[200010],cnt;
int n,m,a[220010];
void update(int &id,int pre,int l,int r,int d,int v) {
    id=++cnt;
    tree[id]=tree[pre];
    tree[id].siz++;
    if(l==r){
        tree[id].siz+=v;
        return;
    }
    int mid=(l+r)/2;
    if(d<=mid) update(tree[id].ls,tree[pre].ls,l,mid,d,v);
    else update(tree[id].rs,tree[pre].rs,mid+1,r,d,v);
}
bool query(int id,int pre,int l,int r,int x,int y) {
    if(x<=l&&y>=r) return tree[id].siz-tree[pre].siz;
    if(tree[id].siz-tree[pre].siz==0) return 0;
    int mid=(l+r)/2,res=0;
    if(x<=mid) res|=query(tree[id].ls,tree[pre].ls,l,mid,x,y);
    if(res) return 1;
    if(y>mid) res|=query(tree[id].rs,tree[pre].rs,mid+1,r,x,y);
    return res;
}
int main(){
	int u,v,x,b;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n; i++) {
        scanf("%d",&a[i]);
        update(rt[i],rt[i-1],0,(1<<18)-1,a[i],1);
    }
    int mid,l,r;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d%d",&b,&x,&u,&v);
        l=0,r=(1<<18)-1;
        for(int j=17;j>=0;j--){
            mid=(l+r)/2;
            if(b&(1<<j))
                if(query(rt[v],rt[u-1],0,(1<<18)-1,max(0,l-x),max(0,mid-x))) r=mid;
                else l=mid+1;
            else {
                if(query(rt[v],rt[u-1],0,(1<<18)-1,max(mid-x+1,0),max(r-x,0))) l=mid+1;
                else r=mid;
            }
        }
        printf("%d\n",l^b);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值