题意: 给定一个序列 ai 有m个询问 l r p k 问区间l r 内 与p距离(绝对值差) 的第k小
比赛的时候 二分找到p在lr区间所处的位置 然后左右k遍历 疯狂超时
复杂度可以去掉一个k
直接二分答案
主席树维护 l r 区间里 [p-mid,p+mid] 的个数 即可
注意这种主席树的经典维护方式!!!!!!区间内维护 ai 属于【x,y】的个数
普通主席树是离散化然后扔进去 T 为 区间下标
这种是以数据从小到大的位次为T
#include <bits/stdc++.h> using namespace std; const int N = 110000; const int M = 2000000; int nodes; int b[N], id[N]; int tot, K; int T[M],lson[M],rson[M],t[M]; inline void up(int x,int l,int r, int pre,int &pos) { pos=++ncnt; lson[pos]=lson[pre]; rson[pos]=rson[pre]; t[pos]=t[pre]+1; if(l==r)return ;int m=l+r>>1; if(x<=m)up(x,l,m,lson[pos],lson[pos]); else up(x,m+1,r,rson[pos],rson[pos]); } void qsum(int L,int R,int x,int y,int l,int r) { if(tot>=K)return ; if(L<=l&&r<=R) { tot+=t[y]-t[x];return ; } int m=l+r>>1; if(L<=m)qsum(L,R,lson[x],lson[y],l,m); if(R>m)qsum(L,R,rson[x],rson[y],m+1,r); } int main() { int ncase; cin>>ncase; while (ncase--) { int prv = 0; int n, m; scanf("%d%d",&n,&m); for (int i = 1; i <= n; i++) scanf("%d",&b[i]), id[i] = i; sort(id + 1, id + n + 1, [](int i, int j) { return b[i] < b[j]; }); sort(b + 1, b + n + 1); ncnt = 0; for (int i = 1; i <= n; i++)up(id[i], 1,n, T[i-1],T[i]); //root[i] = modify(1, 1, n, id[i], root[i - 1]); for (int i = 1; i <= m; i++) { int L, R, p; scanf("%d%d%d%d",&L,&R,&p,&K); L ^= prv, R ^= prv, p ^= prv, K ^= prv; int st = -1, en = 1e8; while (en - st > 1) { int mid = st + en >> 1; int l = lower_bound(b + 1, b + n + 1, p - mid) - b; int r = upper_bound(b + 1, b + n + 1, p + mid) - b - 1; tot = 0; qsum(L,R,T[l-1],T[r],1,n); if (tot >= K) en = mid; else st = mid; } prv = en; printf("%d\n",en); } } return 0; }
多校4 K-th Closest Distance 主席树 二分答案
原文:https://www.cnblogs.com/bxd123/p/11279626.html