题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6621
题目大意:给一个数组,每次给 l ,r, p, k,问区间 [l, r] 的数与 p 的绝对值的第 k小的数是哪个
解题思路:可以二分mid,然后判断在[l,r]区间内是否刚好有k个数大于等于p-mid,小于等于p+mid,判断的话可以直接用主席树,用主席树可以查找出小于等于某个数的个数,找到小于p+mid的个数-小于等于p-mid-1的个数,即为区间[l,r]内小于等于p+mid且大于p-mid的个数。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=100005; int n,m,cnt,root[maxn],a[maxn]; struct node{ int l,r,sum; }T[maxn*40]; vector<int> v; int getid(int x){ return lower_bound(v.begin(),v.end(),x)-v.begin()+1; } void update(int &now,int pre,int l,int r,int pos){ T[++cnt]=T[pre],T[cnt].sum++,now=cnt; if(l==r) return; int mid=(l+r)/2; if(pos<=mid) update(T[now].l,T[pre].l,l,mid,pos); else update(T[now].r,T[pre].r,mid+1,r,pos); } int query(int l,int r,int x,int y,int k){ if(l==r) return T[y].sum-T[x].sum; int mid=(l+r)/2,ans=0; if(k<=mid) ans+=query(l,mid,T[x].l,T[y].l,k); else{ ans+=T[T[y].l].sum-T[T[x].l].sum; ans+=query(mid+1,r,T[x].r,T[y].r,k); } return ans; } int main(){ int t; scanf("%d",&t); while(t--){ cnt=0; v.clear(); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]),v.push_back(a[i]); sort(v.begin(),v.end()),v.erase(unique(v.begin(),v.end()),v.end()); for(int i=1;i<=n;i++) update(root[i],root[i-1],1,n,getid(a[i])); int tmp=0; while(m--){ int L,R,k,p; scanf("%d%d%d%d",&L,&R,&p,&k); L^=tmp; R^=tmp; p^=tmp; k^=tmp; int l=0,r=1e6+9,ans; while(l<=r){ int mid=(l+r)/2; int l1=upper_bound(v.begin(),v.end(),p-mid-1)-v.begin(),r1=upper_bound(v.begin(),v.end(),p+mid)-v.begin(); int x1,x2; if(l1==0) x1=0; else x1=query(1,n,root[L-1],root[R],l1); if(r1==0) x2=0; else x2=query(1,n,root[L-1],root[R],r1); if(x2-x1>=k){ ans=mid; r=mid-1; }else l=mid+1; } printf("%d ",ans); tmp=ans; } } return 0; }