当前位置: 首页 > news >正文

模板 主席树查询区间k小

模板:

有一个长 n n n的序列, m m m个询问,每次查询区间 [ l , r ] [l,r] [l,r]的第 k k k小是多少

Solution:

可持久化线段树查询静态区间 k k k小,利用可持久化线段树的多重线段树,以 O ( n + n l o g n ) O(n+nlogn) O(n+nlogn)的空间存储 n n n棵线段树的特点,来保存一颗前缀权值线段树,随后在 [ l , r ] [l,r] [l,r]的权值线段树上二分

思想:

不妨考虑一个简单问题,我们只查询序列的总体 k k k小,并且限制使用权值线段树来解决问题,显然我们只需要在线段树上二分即可。那要查询 [ l , r ] [l,r] [l,r]的区间 k k k小,我们可以想办法构造出一颗 [ l , r ] [l,r] [l,r]的权值线段树,直接在上面二分即可解决问题。

主席树就是利用这样的思想, [ l , r ] [l,r] [l,r]的权值线段树等价于 [ 1 , r ] [1,r] [1,r]的权值线段树减去 [ 1 , l − 1 ] [1,l-1] [1,l1]的权值线段树,但是多棵线段树难以保存,并且维护每一棵线段树耗费时间巨大,每一棵都是 n l o g n nlogn nlogn,总复杂度至少就是 O ( n 2 l o g n ) O(n^{2}logn) O(n2logn),这是不理想的。可持久化线段树恰恰能保存多颗线段树于一身,并且添加一颗新的线段树时空复杂度都是 O ( l o g n ) O(logn) O(logn),所以可以保存所有的前缀权值线段树在一颗可持久化线段树上,这样只需要在 [ 1 , l − 1 ] [1,l-1] [1,l1] [ 1 , r ] [1,r] [1,r]的差值权值线段树上二分即可。

而每次构建新的权值线段树,就是在 v e r s i o n [ i − 1 ] version[i-1] version[i1]的基础上在 a [ i ] a[i] a[i]的位置上+1即可

二分:

在差值权值线段树上二分,如果左边的总数 ≥ k \geq k k,那么直接在左边查询即可,否则查询右边的 k − t o t k-tot ktot小, t o t tot tot是左边子树的元素个数和

洛谷 P 3834 P3834 P3834
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;

int version[1000005],tree[30000005],lson[30000005],rson[30000005];
int n,m,cnt,vp,a[1000005],b[1000005];
inline int& rs(int x){return rson[x];}
inline int& ls(int x){return lson[x];}

void build(int l,int r,int x)
{
    if(l==r) return;
    int mid=l+r>>1;
    if(!ls(x)) ls(x)=++cnt;
    if(!rs(x)) rs(x)=++cnt;
    build(l,mid,ls(x));
    build(mid+1,r,rs(x));
}

inline void push_up(int x){tree[x]=tree[ls(x)]+tree[rs(x)];}

void modify(int nl,int l,int r,int x,int nx)
{
    if(l==r){tree[nx]=tree[x]+1;return;}
    int mid=l+r>>1;
    if(nl<=mid) ls(nx)=++cnt,rs(nx)=rs(x),modify(nl,l,mid,ls(x),ls(nx));
    else ls(nx)=ls(x),rs(nx)=++cnt,modify(nl,mid+1,r,rs(x),rs(nx));
    push_up(nx);
}

void insert(int k)
{
    version[++vp]=++cnt;
    modify(k,1,n,version[vp-1],version[vp]);
}

int query(int l,int r,int lx,int rx,int k)
{
    if(l==r) return b[l];
    int mid=l+r>>1,tot=tree[ls(rx)]-tree[ls(lx)];
    if(k<=tot) return query(l,mid,ls(lx),ls(rx),k);
    return query(mid+1,r,rs(lx),rs(rx),k-tot);
}

inline int query(int l,int r,int k)
{
    return query(1,n,version[l-1],version[r],k);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin>>n>>m; 
    build(1,n,version[0]=++cnt);//不要写成build(1,n,1),可以写成build(1,n,0)
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        b[i]=a[i];
    }
    sort(b+1,b+1+n);
    for(int i=1;i<=n;i++)
    {
        a[i]=lower_bound(b+1,b+1+n,a[i])-b;
        insert(a[i]);
    }
    while(m--)
    {
        int l,r,k; cin>>l>>r>>k;
        printf("%d\n",query(l,r,k));
    }
    return 0;
}

相关文章:

  • 【JQ】【基础核心】【选择器】
  • 一次带你掌握MVC和MVVM的区别
  • 为什么参与LiveVideoStackCon 2022 北京站
  • 2022年数学建模国赛(A题/B题/C题)评阅要点
  • 大数据讲课笔记1.3 Linux目录操作
  • NumberBox 步进器
  • PythonGUI编程(3) ---- Options选项 Entry单行文本框 Text多行文本框
  • 源表应用之四探针法测量半导体电阻率
  • qs序列化插件
  • 焊缝质量检测数据集
  • 学习C++图像处理最快最好的途径
  • EasyExcel的使用
  • 操作系统实验一 Linux基本操作
  • 【JavaEE初阶】前端篇:HTML(下篇)
  • 中国青年报APP设备注册
  • 分享一款快速APP功能测试工具
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • ES10 特性的完整指南
  • Eureka 2.0 开源流产,真的对你影响很大吗?
  • iOS | NSProxy
  • javascript 总结(常用工具类的封装)
  • Javascript编码规范
  • Java应用性能调优
  • JS实现简单的MVC模式开发小游戏
  • opencv python Meanshift 和 Camshift
  • PermissionScope Swift4 兼容问题
  • Python学习之路16-使用API
  • 计算机常识 - 收藏集 - 掘金
  • 手机端车牌号码键盘的vue组件
  • 微信小程序实战练习(仿五洲到家微信版)
  • 学习使用ExpressJS 4.0中的新Router
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • scrapy中间件源码分析及常用中间件大全
  • 进程与线程(三)——进程/线程间通信
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • ​低代码平台的核心价值与优势
  • (delphi11最新学习资料) Object Pascal 学习笔记---第7章第3节(封装和窗体)
  • (二开)Flink 修改源码拓展 SQL 语法
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (十六)串口UART
  • (四) 虚拟摄像头vivi体验
  • (转)winform之ListView
  • .NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions
  • .NET Framework 和 .NET Core 在默认情况下垃圾回收(GC)机制的不同(局部变量部分)
  • .NET 使用 JustAssembly 比较两个不同版本程序集的 API 变化
  • .net 受管制代码
  • .NET(C#) Internals: as a developer, .net framework in my eyes
  • .NET/C# 在 64 位进程中读取 32 位进程重定向后的注册表
  • .NetCore 如何动态路由
  • .net开发引用程序集提示没有强名称的解决办法
  • .NET学习全景图
  • /bin、/sbin、/usr/bin、/usr/sbin
  • /var/spool/postfix/maildrop 下有大量文件
  • @RequestBody详解:用于获取请求体中的Json格式参数
  • [ C++ ] STL---string类的模拟实现