题目链接: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
#include
#include
#include
#include
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;
}