题意: 给定一个序列 ai 有m个询问 l r p k 问区间l r 内 与p距离(绝对值差) 的第k小
比赛的时候 二分找到p在lr区间所处的位置 然后左右k遍历 疯狂超时
复杂度可以去掉一个k
直接二分答案
主席树维护 l r 区间里 [p-mid,p+mid] 的个数 即可
注意这种主席树的经典维护方式!!!!!!区间内维护 ai 属于【x,y】的个数
普通主席树是离散化然后扔进去 T 为 区间下标
这种是以数据从小到大的位次为T
#include<bits/stdc++.h> using namespace std; const int N=2e5+100; int lson[N<<5],rson[N<<5],T[N],t[N<<5],ncnt; void update(int x,int l,int r,int pre,int &pos) { pos=++ncnt; lson[pos]=lson[pre]; rson[pos]=rson[pre]; t[pos]=t[pre]+1; if(l==r) return ; int m=l+r>>1; if(x<=m) update(x,l,m,lson[pre],lson[pos]); else update(x,m+1,r,rson[pre],rson[pos]); } int qnum(int L,int R,int l,int r,int pre,int pos) { if(L<=l&&r<=R) return t[pos]-t[pre]; int m=l+r>>1,ans=0; if(L<=m) ans+=qnum(L,R,l,m,lson[pre],lson[pos]); if(R>m) ans+=qnum(L,R,m+1,r,rson[pre],rson[pos]); return ans; } int n,m,l,r,k,x,p; int main() { int cas;cin>>cas; while(cas--) { scanf("%d%d",&n,&m); ncnt=0; for(int i=1;i<=n;i++) scanf("%d",&x), update(x,1,1000000,T[i-1],T[i]); int lastans=0; while(m--) { scanf("%d%d%d%d",&l,&r,&p,&k); l^=lastans;r^=lastans;p^=lastans;k^=lastans; int L=0,R=1e9,ans=0; while(L<=R) { int mid=L+R>>1; if(qnum(max(1,p-mid),min(1000000,p+mid),1,1000000,T[l-1],T[r])>=k)R=mid-1,ans=mid; else L=mid+1; } printf("%d ",lastans=ans); } } return 0; }