2019杭电多校第四场 H - K-th Closest Distance(二分加主席树)

题目链接:HDU - 6621

题意:给定n个数字,q次询问,每次给出l,r,p,k。询问区间l,r内所有数字与p的差值的绝对值中排名第k大的是多少。

看到题目给了15秒的时限,再看看题目数据很容易想到单次查询应该是log*log的复杂度,再观察这个答案发现具有单调性,于是想到要二分答案,答案确定后,就可以知道查询区间了,我们只需要查询区间[p-mid,p+mid]内有多少数字即可。

关于单调性:二分的答案越小,查询的区间就越小,产生的值固然也越小。

询问是在线的。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int maxn=2e5+7;

struct Tree{
    int lc,rc;
    int sum;
}tree[maxn*50];

int root[maxn],tot;

int build(int l,int r){
    int k=++tot;
    tree[k].sum=0;
    if(l==r) return k;
    int mid=(l+r)>>1;
    tree[k].lc=build(l,mid);
    tree[k].rc=build(mid+1,r);
    return k;
}

int updata(int p,int l,int r,int id,int val){
    int k=++tot;
    tree[k]=tree[p];
    if(l==r){
        tree[k].sum+=val;
        return k;
    }
    int mid=(l+r)>>1;
    if(id<=mid) tree[k].lc=updata(tree[p].lc,l,mid,id,val);
    else tree[k].rc=updata(tree[p].rc,mid+1,r,id,val);
    tree[k].sum=tree[tree[k].lc].sum+tree[tree[k].rc].sum;
    return k;
}

int myfind(int p,int q,int l,int r,int L,int R){
    if(L>R) return 0;
    if(l>=L&&r<=R) return tree[p].sum-tree[q].sum;
    int mid=(l+r)>>1;
    int res=0;
    if(L<=mid) res+=myfind(tree[p].lc,tree[q].lc,l,mid,L,R);
    if(R>mid) res+=myfind(tree[p].rc,tree[q].rc,mid+1,r,L,R);
    return res;
}

int a[maxn],b[maxn],m;
void quchong(int n){
    sort(b+1,b+1+n);
    m=unique(b+1,b+1+n)-b-1;
}

int getid(int x){
    return lower_bound(b+1,b+1+m,x)-b;
}

int erfen(int l,int r,int x){//第一个大于等于;
    int mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(b[mid]<x) l=mid+1;
        else r=mid-1;
    }
    return l;
}

int erfen2(int l,int r,int x){//最后一个小于等于;
    int mid;
    while(l<=r){
        mid=(l+r)>>1;
        if(b[mid]<=x) l=mid+1;
        else r=mid-1;
    }
    return r;
}

int main(){
    int t;
    scanf("%d",&t);
    int n,q,id;
    int l,r,p,k,hh,ll,rr;
    while(t--){
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        quchong(n);
        root[0]=build(1,m);
        for(int i=1;i<=n;++i)
            root[i]=updata(root[i-1],1,m,getid(a[i]),1);
        int x=0;
        while(q--){
            scanf("%d%d%d%d",&l,&r,&p,&k);
            l=l^x,r=r^x,p=p^x,k=k^x;
            int L=0,R=1000000,mid;
            while(L<=R){
                mid=(L+R)>>1;
                ll=erfen(1,m,p-mid);
                rr=erfen2(1,m,p+mid);
                if(ll>m) --ll;
                if(rr<=0) ++rr;
                hh=myfind(root[r],root[l-1],1,m,ll,rr);
                if(hh>=k) R=mid-1;
                else L=mid+1;
            }
            x=L;
            printf("%d\n",x);

        }

    }

    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值