Coding.|

Hyx'

园龄:5年2个月粉丝:10关注:5

【 静态区间 kth 】 第 k 小数

传送门

题意

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

数据范围

1n105
1m104
|ai|109

题解

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

  • 线段树的左右儿子不再通过堆的存储实现,通过左右指针指向左右儿子
  • 其中当前的值不通过线段树结点的属性体现,体现在各个函数的参数之中
  • 每次的插入操作都时,都只会修改左右指针中可能发生变化的子树,一共log(n)次,所以每次插入在树上都只有一条链会被修改
  • 按照原序列的下标一个一个的插入,插入a1时即第一个版本
    • 询问下标lr中的数的个数的时候利用前缀和的思想做第r个版本的和第l1个版本的差即可
    • 每次查询如果当前左区间差的个数k就递归左子树,否则递归右子树并将k=kcnt
    • 当查询到边界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\n",dis[query(root[r],root[l-1],0,dis.size()-1,k)]);
    }
    return 0;
}

本文作者:Hyx'

本文链接:https://www.cnblogs.com/hhyx/p/13934021.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Hyx'  阅读(143)  评论(0编辑  收藏  举报
编辑推荐:
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
阅读排行:
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 数据库服务器 SQL Server 版本升级公告
· 程序员常用高效实用工具推荐,办公效率提升利器!
· C#/.NET/.NET Core技术前沿周刊 | 第 23 期(2025年1.20-1.26)
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起