HDU 6621 K-th Closest Distance(主席树+二分)

题目链接
题意

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;
}

你可能感兴趣的:(#,二分,可持久化数据结构)