以下均学习于这个老哥的博客https://blog.youkuaiyun.com/qq_41286356/article/details/97958401#commentsedit
题意:给定你N个数字, Q次询问,l,r,p,k,问你在【L,R】区间内的离P第K近的数的距离,强制在线
题解:观察发现可以二分距离,又因为主席树是可持久化的线段树,边更新边建树,最后直接询问即可。 每次查询【L,R】区间内【p-mid,p+mid】有多少数即可。 所以主席树也可以处理[L,R]区间里[l,r]里数的个数
---------------------------------------------------
细节:刚开始不是特别理解 这块我认为只找到了覆盖【pl,pr】的结点怎么能直接返回【L,R】的数的个数那,不是就多了吗
if(pl<=l&&r<=pr) return T[R].sum-T[L].sum;
,后来问了内个老哥才彻底懂了,因为主席树是可持久化的权值线段树,每一个结点都是一个前缀线段树。pl,pr是这棵主席树上查找的权值区间,L和R才是主席树上某两颗线段树,所以在【pl,pr】的结点返回的就是这两颗前缀权值树的差(【L,R】)就是当前区间数的个数。
另一个问题就是 对数据离散化后,二分的p+mid,p-mid也得离散化,需要注意的就是我每次查询都是【p+mid,p-mid】闭区间内
int PL=lower_bound(v.begin(),v.end(),p-mid)-v.begin()+1;
int PR=upper_bound(v.begin(),v.end(),p+mid)-v.begin();
一定得保证这个区间内,所以PL找大于等于的第一个,PR找大于的第一个-1的位置即可。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
vector<int> v;
struct node{
int l;
int r;
int sum;
}T[maxn*50];
int n,m,a[maxn],root[maxn],cnt;
int getid(int x){
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void update(int l,int r,int &x,int y,int pos){
T[++cnt]=T[y];
T[cnt].sum++;
x=cnt;
if(l==r) return ;
int mid=(l+r)/2;
if(pos<=mid) update(l,mid,T[x].l,T[y].l,pos);
else update(mid+1,r,T[x].r,T[y].r,pos);
}
int query(int l,int r,int L,int R,int pl,int pr){
if(pl<=l&&r<=pr) return T[R].sum-T[L].sum;
int mid=(l+r)/2;
int ans=0;
if(pl<=mid) ans+=query(l,mid,T[L].l,T[R].l,pl,pr);
if(pr>mid) ans+=query(mid+1,r,T[L].r,T[R].r,pl,pr);
return ans;
}
int main(){
std::ios::sync_with_stdio(false);
int t;
// cin>>t;
scanf("%d",&t);
while(t--){
//cin>>n>>m;
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());
int sz=v.size();
for(int i=1;i<=n;i++){
update(1,n,root[i],root[i-1],getid(a[i]));
}
int ans=0;
while(m--){
int l,r,p,k;// cin>>l>>r>>p>>k;
scanf("%d%d%d%d",&l,&r,&p,&k);
l=l^ans;r=r^ans;p=p^ans;k=k^ans;
if(l>r) swap(l,r);
int L=0;int R=1000005;
while(L<=R){
int mid=(L+R)/2;
int PL=lower_bound(v.begin(),v.end(),p-mid)-v.begin()+1;
int PR=upper_bound(v.begin(),v.end(),p+mid)-v.begin();
int tmp=query(1,sz,root[l-1],root[r],PL,PR);
if(tmp>=k){
ans=mid;
R=mid-1;
}else L=mid+1;
}
//cout<<ans<<endl;
printf("%d\n",ans);
}
}
}