题目链接
题目大意
有一个序列,有m次询问,每次询问都是有一个(l,r,p,k)数组中的数减去p的绝对值的第k大的数是多少。
解题思路
我们去二分这个绝对值,也就是在【l,r】这个区间里找到一个【p-mid,p+mid】里面有k个数,找到最小的mid,就是答案了
用主席树去维护。
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=1e5+5;
const int maxn=1e6;
struct node
{
int l,r,sum;
};
node e[N*100];
int a[maxn+10],b[maxn+10],root[maxn+10],cnt,ans,n,m;
void init()
{
cnt=1;
ans=0;
root[0]=0;
e[0].l=e[0].r=e[0].sum=0;
}
void update(int num,int &rt,int l,int r)
{
e[cnt++]=e[rt];
rt=cnt-1;
e[rt].sum++;
if(l==r)
return ;
int mid=(l+r)/2;
if(num<=mid)
update(num,e[rt].l,l,mid);
else
update(num,e[rt].r,mid+1,r);
}
int que(int al,int ar,int l,int r,int i,int j)
{
if(al<=l&&ar>=r)
return e[j].sum-e[i].sum;
else
{
int mid=(l+r)/2;
int res=0;
if(al<=mid)
res+=que(al,ar,l,mid,e[i].l,e[j].l);
if(ar>mid)
res+=que(al,ar,mid+1,r,e[i].r,e[j].r);
return res;
}
}
int solve(int ll,int rr,int p,int k)
{
int l=0,r=maxn,ans1;
while(l<=r)
{
int mid=(l+r)/2;
int le=p-mid,re=mid+p;
//printf("%d %d\n",root[l-1],root[r]);
int c=que(max(1,le),min(re,maxn),1,maxn,root[ll-1],root[rr]);
if(c>=k)
{
r=mid-1;
ans1=mid;
}
else
l=mid+1;
}
return ans1;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
init();
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
root[i]=root[i-1];
update(a[i],root[i],1,maxn);
}
int l=0,r=0,p=0,k=0,x=0;
for(int i=1;i<=m;i++)
{
scanf("%d %d %d %d",&l,&r,&p,&k);
l^=x;r^=x;p^=x;k^=x;
x=solve(l,r,p,k);
printf("%d\n",x);
}
}
return 0;
}