n个整数的数列,多组询问,求区间减去一个值后绝对值的第k小。
区间第K小主席树就行,这题要求减去一个值后绝对值第k小原先想着二分找到最接近0的左右k个元素,但是这样复杂度多了一个K有点大。
可以二分答案,如果p-mid,p+mid区间内元素等于k个说明mid是答案,区间元素个数大于等于k作为判断条件具有单调性,可以据此二分,这样就优化掉了一个k。
#include
using namespace std;
int n, m;
int a[100005];
int root[100005], id, N = 1000000;
struct Node {
int l, r, sum;
}t[1000005*30];
void build(int l, int r, int &x, int y, int pos) {
t[++id] = t[y];
++t[id].sum;
x = id;
if(l == r) return;
int mid = l+r >> 1;
if(pos <= mid) build(l,mid,t[x].l,t[y].l,pos);
else build(mid+1,r,t[x].r,t[y].r,pos);
}
int query(int l, int r, int x, int y, int ql, int qr) {
if(ql <= l && r <= qr) return t[y].sum-t[x].sum;
int mid = l+r >> 1, res = 0;
if(ql <= mid) res += query(l,mid,t[x].l,t[y].l,ql,qr);
if(qr > mid) res += query(mid+1,r,t[x].r,t[y].r,ql,qr);
return res;
}
int main() {
int T;
for(scanf("%d",&T); T; --T) {
id = 0;
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; ++i) scanf("%d",&a[i]);
for(int i = 1; i <= n; ++i) build(1,N,root[i],root[i-1],a[i]);
int ans = 0;
while(m--) {
int l, r, p, k;
scanf("%d%d%d%d",&l,&r,&p,&k);
l ^= ans, r ^= ans, p ^= ans, k ^= ans;
int L = 0, R = N, mid;
while(L <= R) {
mid = L+R >> 1;
int fq = query(1,N,root[l-1],root[r],max(p-mid,1),min(p+mid,N));
if(fq >= k) ans = mid, R = mid-1;
else L = mid+1;
}
printf("%d\n",ans);
}
}
return 0;
}