概念

可持久化线段树又叫主席树,之所以叫主席树是因为这东西是fotile主席创建出来的。
可持久化数据结构思想,就是保留整个操作的历史,即,对一个线段树进行操作之后,保留访问操作前的线段树的能力。
最简单的方法,每操作一次,建立一颗新树。这样对空间的需求会很大。而注意到,对于点修改,每次操作最多影响 $O(\log n)$ 个节点,于是,其实操作前后的两个线段树,结构一样,因此可以共享未被影响的节点,被影响的就新建节点。于是,这样的线段树,每次操作需要O(log2(n))的空间。
线段树对于每个n的分解是唯一的,所以n相同的线段树结构相同,这也是实现可持久化线段树的基础。

分析

对于区间第K小,
先用 sort + unique 进行离散化操作;然后以离散化后的元素作为底层(接下来大概就是用线段树按顺序记录每个数字出现的次数)。
关键来了:以不同的区间建立不同的版本,及1..1为1号,1..2为2号,1..7为7号,以此类推。 查询区间 l..r 需要查询两个版本:ver[l-1]ver[r],同步查询两棵树,并对查询到的内容相减便是l..r中该范围内的数据(以数字出现字数为关键词)。相当于线段树的前缀和
#include<bits/stdc++.h>
using namespace std; const int maxn = 2e5 + ;
int n, m, size_disc; //size_disc是离散化之后的长度
int n_init[maxn], n_disc[maxn]; //原数组 离散化后的数组
int rt[maxn], lc[maxn << ], rc[maxn << ], sum[maxn << ]; //rt:不同版本的根节点 lc/rc: 左儿子、右儿子(公用) sum: 和(公用)
int node_cnt, pnt_disc; //node总计数, pnt_disc: A中数字对应B中的值 void build(int& last_node, int l, int r)
{
last_node = ++ node_cnt;
sum[last_node] = ;
if(l == r) return;
int mid = (l + r) >> ;
build(lc[last_node], l, mid);
build(rc[last_node], mid+, r);
} int modify(int pre_rt, int l, int r)
{
int new_rt = ++node_cnt;
lc[new_rt] = lc[pre_rt];
rc[new_rt] = rc[pre_rt];
sum[new_rt] = sum[pre_rt] + ; int mid = (l + r) >> ;
if(l == r) return new_rt;
if(mid >= pnt_disc) lc[new_rt] = modify(lc[new_rt], l, mid);
else rc[new_rt] = modify(rc[new_rt], mid+, r);
return new_rt;
} int query(int rt1, int rt2, int k, int l, int r)
{
//printf("rt1:%d rt2:%d k:%d l:%d r:%d ", rt1, rt2, k, l, r);
if(l == r) return l;
int mid = (l + r) >> ;
int tmp = sum[lc[rt2]] - sum[lc[rt1]];
//int tmp=sum[lc[rt2]]-sum[lc[rt1]]; //printf("tmp:%d k:%d\n", tmp, k); int ans;
if(tmp >= k) ans = query(lc[rt1], lc[rt2], k, l, mid);
else ans = query(rc[rt1], rc[rt2], k-tmp, mid+, r);
return ans;
} void print_debug()
{
printf("node_cnt: %d\n", node_cnt);
for(int i = ;i <= node_cnt;i++)
printf("%d lc:%d rc:%d sum:%d\n", i, lc[i], rc[i], sum[i]);
} int main()
{
scanf("%d%d", &n, &m);
for(int i = ;i <= n;i++)
{
scanf("%d", &n_init[i]);
n_disc[i] = n_init[i];
}
sort(n_disc+, n_disc+n+); //先排序再进行离散化
size_disc = unique(n_disc+, n_disc+n+) - (n_disc+); node_cnt = ;
build(rt[], , size_disc);
for(int i = ;i <= n;i++)
{
pnt_disc = lower_bound(n_disc+, n_disc+size_disc+, n_init[i]) - n_disc; //改了1
rt[i] = modify(rt[i-], , size_disc); //只在上一个版本的基础上修改
} for(int i = ;i <m;i++)
{
int l, r, k;
scanf("%d%d%d", &l, &r, &k);l--;
int ans = query(rt[l], rt[r], k, , size_disc);
printf("%d\n", n_disc[ans]);
}
}

其中,遍历1~n建立n棵线段树的过程如下:

其中第i棵是建立在i-1棵的基础上,加上它们的n相同,结构也完全相同,两颗线段树相减就是这两个操作之间的变化值。

参考链接:

1. https://yfli.site/note14/

2. https://www.luogu.org/problemnew/solution/P3834

区间第K小——可持久化线段树模板的更多相关文章

  1. POJ- 2104 hdu 2665 (区间第k小 可持久化线段树)

    可持久化线段树 也叫函数式线段树也叫主席树,其主要思想是充分利用历史信息,共用空间 http://blog.sina.com.cn/s/blog_4a0c4e5d0101c8fr.html 这个博客总 ...

  2. [POJ2104] 区间第k大数 [区间第k大数,可持久化线段树模板题]

    可持久化线段树模板题. #include <iostream> #include <algorithm> #include <cstdio> #include &l ...

  3. 区间第k大问题 权值线段树 hdu 5249

    先说下权值线段树的概念吧 权值平均树 就是指区间维护值为这个区间内点出现次数和的线段树 用这个加权线段树 解决第k大问题就很方便了 int query(int l,int r,int rt,int k ...

  4. 序列内第k小查询(线段树)

    最近请教了一下大佬怎么求序列内第k大查询,自己又捣鼓了一下,虽然还没有懂得区间第k大查询,不过姑且做一个记录先吧 因为每个元素大小可能很大而元素之间不连续,所以我们先离散化处理一下,程序中的ori[ ...

  5. hdu 4417 区间内比h小的数 线段树

    题意求区间内比h小的数的个数 将所有的询问离线读入之后,按H从小到大排序.然后对于所有的结点也按从小到大排序,然后根据查询的H,将比H小的点加入到线段树,然后就是一个区间和. 2015-07-27:专 ...

  6. HDU 2665.Kth number-可持久化线段树(无修改区间第K小)模板 (POJ 2104.K-th Number 、洛谷 P3834 【模板】可持久化线段树 1(主席树)只是输入格式不一样,其他几乎都一样的)

    Kth number Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  7. SPOJ Meteors - 可持久化线段树 - 二分法

    Byteotian Interstellar Union (BIU) has recently discovered a new planet in a nearby galaxy. The plan ...

  8. 洛谷P3834【模板】可持久化线段树 1(主席树)

    题目背景 这是个非常经典的主席树入门题--静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输 ...

  9. [DP][SA][可持久化线段树]黑红兔

    源自 xyz32768 菜鸡的 FJ 省冬令营模拟赛题 原题 CF1063F Statement 给定一个长度为 \(n\) 的字符串 \(s\),仅包含小写英文字母 要从中从左往右选出若干段不相交的 ...

随机推荐

  1. leveldb单元测试之宏定义源码剖析

    前言 leveldb 是一个库,没有 main() 函数入口, 故非常难理清其中的代码逻辑.但好在库中有非常多的单元测试代码,帮助读者理解其中的各个模块的功能.然而,测试代码个人觉得一开始看时非常费解 ...

  2. H3C路由器登录策略专题讲解

    password-control login-attempt login-times [ exceed { lock | lock-time time | unlock } ] undo passwo ...

  3. Django 用Session和Cookie分别实现记住用户登录状态

    简介 由于http协议的请求是无状态的.故为了让用户在浏览器中再次访问该服务端时,他的登录状态能够保留(也可翻译为该用户访问这个服务端其他网页时不需再重复进行用户认证).我们可以采用Cookie或Se ...

  4. 【AtCoder】AGC004

    AGC004 A - Divide a Cuboid 看哪一维是偶数,答案是0,否则是三个数两两组合相乘中最小的那个 #include <bits/stdc++.h> #define fi ...

  5. 【51nod】2591 最终讨伐

    [51nod]2591 最终讨伐 敲51nod是啥评测机啊,好几次都编译超时然后同一份代码莫名奇妙在众多0ms中忽然超时 这道题很简单就是\(M\)名既被诅咒也有石头的人,要么就把石头给没有石头被诅咒 ...

  6. Java考题知识点

    挑战10个最难回答的Java面试题(附答案) - 里奥ii的文章 - 知乎 https://zhuanlan.zhihu.com/p/79186037 1.java的基本编程单元是类,基本存储单元是变 ...

  7. 文件 open 方法

    文件对象方法: 文件对象方法  执行操作 f.close()    关闭文件 f.read([size=-1]) 从文件读取size个字符,当未给定size或给定负值的时候, 读取剩余的所有字符,然后 ...

  8. java包装类的缓存机制(转)

    出处: java包装类的缓存机制 java 包装类的缓存机制,是在Java 5中引入的一个有助于节省内存.提高性能的功能,只有在自动装箱时有效 Integer包装类 举个栗子: Integer a = ...

  9. el-table el-column selection disable

    几个要点: 1.通过 selectable 绑定 2.绑定的方法只能返回0/1 <el-table-column type="selection" width="5 ...

  10. 如何给Swagger加注释

    在Startup.cs文件中的ConfigureServices()方法中添加如下代码即可 services.AddSwaggerGen(options => { options.Swagger ...