BZOJ4571

  这道题的简化版是没有加d的。如果是那样的话,我们只需要做一个可持久化trie树就好了。把要异或的数划分为二进制,从高位向低位匹配,如果当前位为0,我们查询区间内的数当前位能否为1;如果当前位为1,我们查询区间内的数当前位能否为0。具体来说,假设现在已经匹配了x(这个x是拿来与b匹配的x而不是匹配后的值),现在要匹配第h位,并且b的第h位为1,我们要确定当前位能否为0,其实就是找【x,x+2^h-1】这个区间内的siz是否大于0。h位为0,同理。那么它加了一个d的话,好办,只需要把原来那个区间同时减去d就好了。

#include<bits/stdc++.h>

using namespace std;

const int N=2e5+10;

const int M=1e6+10;

const int sz=262144;

int n,m,now,tot,ls[M],rs[M],a[N],sum[M],rt[N];

void update(int pre,int &o,int l,int r,int x){

    o=++tot;ls[o]=ls[pre];rs[o]=rs[pre];

    sum[o]=sum[pre]+1;

    if(l==r) return ;

    int mid=l+r>>1;

    if(x<mid) update(ls[pre],ls[o],l,mid,x);

    else update(rs[pre],rs[o],mid+1,r,x);

}

int query(int last,int now,int l,int r,int L,int R){

    if(l>=L&&r<=R) return sum[now]-sum[last];

    if(r<L||l>R) return 0;

    int mid=l+r>>1;

    return query(ls[last],ls[now],l,mid,L,R)+query(rs[last],rs[now],mid+1,r,L,R);

}

int main(){

    scanf("%d%d",&n,&m);

    for(int i=1;i<=n;++i){

        scanf("%d",&a[i]);

        update(rt[i-1],rt[i],1,sz,a[i]);

    }

    for(int i=1;i<=m;++i){

        int b,d,l,r;scanf("%d%d%d%d",&b,&d,&l,&r);

        now=0;

        for(int k=17;k>=0;--k){

            if(b&(1<<k)){

                if(query(rt[l-1],rt[r],1,sz,max(0,now-d),max(0,now+(1<<k)-1-d))<=0)

                if(query(rt[l-1],rt[r],1,sz,max(0,now+(1<<k)-d),max(0,now+(1<<(k+1))-1-d))>0) now+=1<<k;

            }else{

                if(query(rt[l-1],rt[r],1,sz,max(0,now+(1<<k)-d),max(0,now+(1<<(k+1))-1-d))>0) now+=1<<k;

            }

        }

        printf("%d\n",now^b);

    }

    return 0;

}

 

speech.gif posted on 2019-01-04 16:07 kgxpbqbyt 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值