hdu 6621 多校第四场 K-th Closest Distance 主席树

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6621

 

题意:

       给你1e5个数和1e5个询问,每个询问给你一个区间L,R 一个质数P和一个数K,你要找到数组中区间L到R中和P距离第k小的值(P和一个数x的距离为abs(P-x))。

 做法:

       第k小其实听着就很像是主席树的样子,但是这里是和一个固定的数的距离,那么我们其实是可以利用主席树中num数组的大小的,毕竟主席树中的历史记录版本root还是在这道题中可以被应用的(对应的就是root[R]和root[L-1]),那么我们该如何找到答案呢,答案是二分,因为这个距离越大,可以包括的数字是越多的,该单调性满足二分的要求,我们可以二分距离mid,然后区间[p-mid,p+mid]找到对应区间去查询数字是否有k个就可以啦。

      注意强制在线!(差点坑了队友,括弧其实还是坑了嘤嘤嘤括弧)。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=1e5+10;
int root[maxn*40],n,q;
int cnt,a[maxn];
int tmp[maxn],ct;
struct node{
    int lson,rson,num;
}e[maxn*40];
int gain(int x){
    return lower_bound(tmp+1,tmp+1+ct,x)-tmp;
}
void update(int l,int r,int &now,int pre,int a){
    now=++cnt;
    e[now]=e[pre];
    e[now].num++;
    if(l==r) return ;
    int mid=(l+r)/2;
    if(a<=mid) update(l,mid,e[now].lson,e[pre].lson,a);
    else  update(mid+1,r,e[now].rson,e[pre].rson,a);
}
int query(int l,int r,int rt,int ql,int qr){
    if(ql<=l&&r<=qr) return e[rt].num;
    int mid=(l+r)/2;
    int ret=0;
    if(ql<=mid) ret+=query(l,mid,e[rt].lson,ql,qr);
    if(qr>mid) ret+=query(mid+1,r,e[rt].rson,ql,qr);
    return ret;
}
int ck(int l,int r,int p,int k,int ql,int qr,int mid){
    int le=lower_bound(tmp+1,tmp+1+ct,p-mid)-tmp,ri=upper_bound(tmp+1,tmp+1+ct,p+mid)-tmp-1;
    if(le>ri) return 0;
    int aim=query(1,ct,root[qr],le,ri)-(ql==1?0:query(1,ct,root[ql-1],le,ri));
    return aim>=k;
}
int main(){
    int T; scanf("%d",&T);
    while(T--){
        cnt=0;
        e[cnt].lson=e[cnt].rson=e[cnt].num=0;
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            tmp[i]=a[i];
        }
        sort(tmp+1,tmp+1+n);
        ct=unique(tmp+1,tmp+1+n)-tmp-1;
        for(int i=1;i<=n;i++){
            int id=gain(a[i]);
            update(1,ct,root[i],root[i-1],id);
        }
        int lst=0;
        while(q--){
            int l,r,p,k;

            scanf("%d%d%d%d",&l,&r,&p,&k);
            l^=lst,r^=lst,p^=lst,k^=lst;
            int L=0,R=1e6,ans=-1;
            while(L<=R){
                int mid=(L+R)/2;
                if(ck(L,R,p,k,l,r,mid)){
                    ans=mid;
                    R=mid-1;
                }
                else L=mid+1;
            }
            printf("%d\n",ans);
            lst=ans;
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值