概念

可持久化线段树又叫主席树,之所以叫主席树是因为这东西是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. 使用Dreamweaver制作简单网站(二)

    继续上周没完成的 一.新建iframe.css 1.点击文件-选择新建-css 2.ctrl+s保存为iframe.css 在style文件夹下. 3.回到main.html 右键选择-附加样式表,选 ...

  2. javascript当中类型转换,typeof的用法

    1)类型转换,typeof的用法 例 3.1.1 <HTML><head>    <meta http-equiv="content-type" co ...

  3. Spark学习一:Spark概述

    1.1 什么是Spark ​ Apache Spark 是专为大规模数据处理而设计的快速通用的计算引擎. ​ 一站式管理大数据的所有场景(批处理,流处理,sql) ​ spark不涉及到数据的存储,只 ...

  4. c++:论如何成功把自己搞懵【二叉树特辑①】(不定期更新)

    并不正经的前言 以前我这个小白看OI的书,老觉得有些东西很高端(看不懂的自然就很高端[滑稽]):什么栈啊,位运算啊,二叉树啊.有些东西我学了之后也很迷糊(真的不是因为傻?),做题的时候总是可以把自己搞 ...

  5. 20190728-Python爬取视频&切割视频&视频加水印

    1.视频爬取 1.下载视频的源码如下: import os import requests from bs4 import BeautifulSoup import threading from bj ...

  6. python3.7 lxml4.2.5 etree xpath 的使用

    #2019年10月14日11:08:49 from lxml import html etree = html.etree html = etree.HTML(response_dl.content) ...

  7. CSP-S2019「Symphony」

    NOTICE:如觉得本文有什么错误或不妥之处,欢迎评论区以及私信交流,反对乱喷,如有一些让人不爽的评论或人身攻击,带来的后果本人一律不负责 准备工作 Day-inf~Day-3 000 every d ...

  8. selenium 12306模拟登陆

    代码应用场景 :基于第三方打码网站模拟登陆12306 验证码识别 基于第三方平台超级鹰识别 超级鹰官网:http://www.chaojiying.com/user/ 超级鹰使用流程: 注册 登陆(用 ...

  9. MySQL cmd操作

    1.开启关闭服务 net start mysql net stio mysql 2.登陆 在CMD命令窗口敲入命令 mysql -hlocalhost -uroot -p 后按回车(注意这里的&quo ...

  10. vue 之this.$router.push、replace、go的区别

    一.this.$router.push 说明:跳转到指定URL,向history栈添加一个新的记录,点击后退会返回至上一个页面 使用: this.$router.push('/index') this ...