【 静态区间 kth 】 第 k 小数

传送门

题意

给定一个长度为(n)的整数序列,下标为(1sim n)
(m)个操作,每次给定((l,r,k)),表示询问下标为(lsim r)的区间内第(k)小的数

数据范围

(1leq nleq 10^{5})
(1leq mleq 10^{4})
(|a_{i}|leq 10^{9})

题解

值域很大,建立权值线段树,权值线段树要求离散化后的值之间的大小关系不发生改变进行离散化后通过二分查找找到离散化后的下标值

  • 线段树的左右儿子不再通过堆的存储实现,通过左右指针指向左右儿子
  • 其中当前的值不通过线段树结点的属性体现,体现在各个函数的参数之中
  • 每次的插入操作都时,都只会修改左右指针中可能发生变化的子树,一共(log(n))次,所以每次插入在树上都只有一条链会被修改
  • 按照原序列的下标一个一个的插入,插入(a_{1})时即第一个版本
    • 询问下标(lsim r)中的数的个数的时候利用前缀和的思想做第(r)个版本的和第(l-1)个版本的差即可
    • 每次查询如果当前左区间差的个数(geq k)就递归左子树,否则递归右子树并将(k=k-cnt)
    • 当查询到边界(l=r)时,即当前值即第(r)个值,其表示的时原值离散化后的值,即其在离散化后的数中的下标

Code

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<=n;i++)

const int N=1e5+10;
struct node{
    int l,r;
    int cnt;
}tr[N*4+N*17];
int a[N];
int root[N],id;
vector<int>dis;
int find(int x){
    return lower_bound(dis.begin(),dis.end(),x)-dis.begin();
}
// 返回的是当前区间的指针
int build(int l,int r){
    int now=++id;
    if(l==r) return now;
    int mid=l+r>>1;
    tr[now].l=build(l,mid);// 递归获得右子树指针
    tr[now].r=build(mid+1,r);
    return now;
}

// pre 表示上一版本的指针,l,r表示离散化后的值域范围,x表示当前插入节点离散化后的值
int insert(int pre,int l,int r,int x){
    int now=++id;
    tr[now]=tr[pre];
    if(l==r){
        tr[now].cnt++;
        return now;
    }
    int mid=l+r>>1;
    // 只修改左指针
    if(x<=mid) tr[now].l=insert(tr[pre].l,l,mid,x); 
    // 只修改右指针
    else tr[now].r=insert(tr[pre].r,mid+1,r,x);

    tr[now].cnt=tr[tr[now].l].cnt+tr[tr[now].r].cnt;
    return now; // 回溯插入后的节点指针
}

int query(int aft,int pre,int l,int r,int k){
    if(l==r) return r;
    int cnt=tr[tr[aft].l].cnt-tr[tr[pre].l].cnt;
    int mid=l+r>>1;
    if(k<=cnt) return query(tr[aft].l,tr[pre].l,l,mid,k);
    else return query(tr[aft].r,tr[pre].r,mid+1,r,k-cnt);
}

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    rep(i,1,n) {
        scanf("%d",&a[i]);
        dis.push_back(a[i]);
    }
    sort(dis.begin(),dis.end());
    dis.erase(unique(dis.begin(),dis.end()),dis.end());

    root[0]=build(0,dis.size()-1); // 只建树,不插入节点
    rep(i,1,n)
        root[i]=insert(root[i-1],0,dis.size()-1,find(a[i]));
    while(m--){
        int l,r,k;
        scanf("%d%d%d",&l,&r,&k);
        printf("%d
",dis[query(root[r],root[l-1],0,dis.size()-1,k)]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/hhyx/p/13934021.html