题意:给一个数组a,每次询问,给定L, R, p, K,求[ L , R ] 中的数与p做差的绝对值的第k小。
思路:对数组a建立主席树(不用离散化),对于每次询问,二分答案,如果 [ L , R ] 区间中的 [ p - mid , p + mid ] 范围内的数大于K,则说明二分的答案偏大,需要缩小区间;如果等于K,也需要缩小,因为要找到精确的值;小于K则需要放大区间。(可以画数轴理解)
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e5+10; 4 const int N=1e6; 5 struct node{ 6 int val,l,r; 7 }tree[maxn*22]; 8 int root[N]; 9 int tot; 10 int n,m; 11 void update(int &p,int x,int l,int r,int rt){ 12 p=++tot; 13 tree[p]=tree[rt]; 14 tree[p].val++; 15 if(l==r) return; 16 int mid=(l+r)>>1; 17 if(x<=mid) update(tree[p].l,x,l,mid,tree[rt].l); 18 if(x>mid) update(tree[p].r,x,mid+1,r,tree[rt].r); 19 } 20 int query(int u,int v,int x,int y,int l,int r){ 21 if(x<=l&&r<=y){ 22 return tree[v].val-tree[u].val; 23 } 24 int ans=0; 25 int mid=(l+r)>>1; 26 if(x<=mid) ans+=query(tree[u].l,tree[v].l,x,y,l,mid); 27 if(y>mid) ans+=query(tree[u].r,tree[v].r,x,y,mid+1,r); 28 return ans; 29 } 30 int solve(int L,int R,int x,int y){ 31 x=max(x,1); 32 y=min(y,N); 33 return query(root[L-1],root[R],x,y,1,N); 34 } 35 int main() 36 { 37 int T; 38 scanf("%d",&T); 39 while(T--){ 40 tot=0; 41 scanf("%d%d",&n,&m); 42 for(int i=1;i<=n;i++){ 43 int x; 44 scanf("%d",&x); 45 update(root[i],x,1,N,root[i-1]); 46 } 47 int ans=0; 48 for(int i=1;i<=m;i++){ 49 int L,R,p,K; 50 scanf("%d%d%d%d",&L,&R,&p,&K); 51 L^=ans,R^=ans,p^=ans,K^=ans; 52 int l=0,r=N,mid; 53 while(r>=l){ 54 mid=(l+r)>>1; 55 if(solve(L,R,p-mid,p+mid)>=K) 56 r=mid-1; 57 else l=mid+1; 58 } 59 ans=l; 60 printf("%d ",ans); 61 } 62 } 63 return 0; 64 }