题意
对于大小为n的正整数数组a,有q次询问,每次询问给出整数数p、k,要求输出离p第k近的数字,强制在线。
思路
主席树维护区间 \([l, r]\) 数字出现次数,二分答案。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
struct node {
int l, r, val;
}tr[maxn*21];
int root[maxn], tot, a[maxn], b[maxn];
void build(int l, int r, int &x) {
x = ++tot;
if(l==r) return;
int mid = l+r>>1;
build(l, mid, tr[x].l); build(mid+1, r, tr[x].r);
}
void update(int l, int r, int x, int &y, int p, int val=1) {
y = ++tot;
tr[y] = tr[x];
tr[y].val += val;
// cout << l << " " << r << " " << tr[y].val << endl;
if(l==r) return;
int mid = l+r>>1;
if(p<=mid) update(l, mid, tr[x].l, tr[y].l, p, val);
else update(mid+1, r, tr[x].r, tr[y].r, p, val);
}
int query(int l, int r, int x, int y, int L, int R) {
if(L<=l && r<=R) return tr[y].val-tr[x].val;
int mid=l+r>>1, ans=0;
if(L<=mid) ans=query(l, mid, tr[x].l, tr[y].l, L, R);
if(R>mid) ans+=query(mid+1, r, tr[x].r, tr[y].r, L, R);
return ans;
}
int T, n, m;
void read(int &x) {
x=0; char ch, c=getchar();
while(c<'0' || c>'9') ch=c, c=getchar();
while(c>='0' && c<='9') x=x*10+c-'0', c=getchar();
if(ch=='-') x=-x;
}
int main() {
// freopen("in.txt", "r", stdin);
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
tot=0;
build(1, maxn, root[0]);
for (int i=1; i<=n; ++i) {
scanf("%d", a + i);
b[i] = a[i];
}
sort(b+1, b+1+n);
int cnt = unique(b+1, b+1+n)-b-1;
for (int i=1; i<=n; ++i) {
a[i] = lower_bound(b+1, b+cnt+1, a[i])-b+1;
update(1, maxn, root[i-1], root[i], a[i]);
}
int ans=0;
for (int L, R, P, K, i=1; i<=m; ++i) {
scanf("%d%d%d%d", &L, &R, &P, &K);
L^=ans, R^=ans, P^=ans, K^=ans;
if(L>R) swap(L, R);
int l=0, r=1e6+10;
while(l<r) {
int mid=l+r>>1;
int ll = lower_bound(b+1, b+1+cnt, P-mid)-b+1;
int rr = upper_bound(b+1, b+1+cnt, P+mid)-b;
int sum = query(1, maxn, root[L-1], root[R], ll, rr);
if(sum >= K) r=mid;
else l=mid+1;
}
printf("%d\n", l);
ans = l;
}
}
return 0;
}