可持久化线段树--主席树

浅谈可持久化线段树--主席树

权值线段树

权值线段树和普通线段树不一样的地方就是在于 它的结点存储的是区间内数的个数

这个线段树的好处就在于我们可以根据 左子树 和 右子树 的大小从而进行 查找某个数的排名 或者 查找排名为rk的数

可持久化的含义

可持久数据结构主要指的是我们可以查询历史版本的情况并支持插入,利用使用之前历史版本的数据结构来减少对空间的消耗(能够对历史进行修改的是函数式)。

主席树的建树过程:

最开始的时候就是一个空树

然后我们再插入一个元素3

再加入一个元素 1

模版一 :求区间第K大

我们考虑查询。

例如我们插入: 1 5 2 6 3 7 4

要查询[2, 5]中第3大的数我们首先把第1棵线段树和第5棵拿出来。

根据前面说的插入操作 我们最终得到的线段树是这样的:

要查询[2, 5]中第3大的数我们首先把第1棵线段树和第5棵拿出来。

然后我们发现,将对应节点的数相减,刚刚好就是[2, 5]内某个范围内的数的个数。比如[1, 4]这个节点相减是2,就说明[2. 5]内有2个数是在1~4范围内(就是2, 3)。

所以对于一个区间[l, r],我们可以每次算出在[l, mid]范围内的数,如果数量>=k(k就是第k大),就往左子树走,否则就往右子树走。

 1 #include <stdio.h>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <string.h>
 5 #include <vector>
 6 #include <random>
 7
 8 const int maxn = 2e5 + 10;
 9
10 int ls[maxn<<5],rs[maxn<<5],sum[maxn<<5],rt[maxn<<5];
11 int cnt;
12
13 void init() {
14     memset(sum,0, sizeof(sum));
15     cnt  = 0;
16 }
17 int build(int l, int r){
18     int root = ++ cnt;
19     if(l == r) return root;
20     int mid = (l + r) >> 1;
21     ls[root] = build(l, mid);
22     rs[root] = build(mid + 1, r);
23     return root;
24 }
25 int update(int k, int l, int r, int root){
26     int id = ++ cnt;
27     ls[id] = ls[root]; rs[id] = rs[root]; sum[id] = sum[root] + 1;
28     if(l == r) return id;
29     int mid = (l + r) >> 1;
30     if(k <= mid) ls[id] = update(k, l, mid, ls[id]);
31     if(k > mid) rs[id] = update(k, mid + 1, r, rs[id]);
32     return id;
33 }
34 int query(int k, int u, int v, int l, int r)
35 {
36     int mid = (l + r) >> 1;
37     int x = sum[ls[v]] - sum[ls[u]];
38     if(l == r) return l;
39     if(k <= x) return query(k, ls[u], ls[v], l, mid);
40     if(k > x) return query(k - x, rs[u], rs[v], mid + 1, r);
41 }
42
43 std::vector<int> v;
44 int getid(int x) {
45     return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
46 }
47
48 int arr[maxn];
49 int main() {
50     int n,m;
51     scanf("%d%d",&n,&m);
52     for (int i=1;i<=n;i++) {
53         scanf("%d",&arr[i]);
54         v.push_back(arr[i]);
55     }
56     std::sort(v.begin(),v.end());
57     v.erase(std::unique(v.begin(),v.end()),v.end());
58     int len = v.size();
59     rt[0] = build(1,len);
60     for (int i=1;i<=n;i++) {
61         rt[i] = update(getid(arr[i]),1,len,rt[i-1]);
62     }
63     while (m--) {
64         int l,r,k;
65         scanf("%d%d%d",&l,&r,&k);
66         printf("%d\n",v[query(k,rt[l-1],rt[r],1,len)-1]);
67     }
68 }

原文地址:https://www.cnblogs.com/-Ackerman/p/11560037.html

时间: 2024-08-29 19:13:41

可持久化线段树--主席树的相关文章

hdu 2665 可持久化线段树求区间第K大值(函数式线段树||主席树)

http://acm.hdu.edu.cn/showproblem.php?pid=2665 Problem Description Give you a sequence and ask you the kth big number of a inteval. Input The first line is the number of the test cases. For each test case, the first line contain two integer n and m (

可持久化数据结构之主席树

转自:http://finaltheory.info/?p=249 HomeACM可持久化数据结构之主席树 06十2013 可持久化数据结构之主席树 Written by FinalTheory on. Posted in ACM 引言 首先引入CLJ论文中的定义: 所谓的“持久化数据结构”,就是保存这个数据结构的所有历史版本,同时利用它们之间的共用数据减少时间和空间的消耗. 本文主要讨论两种可持久化线段树的算法思想.具体实现以及编码技巧. 核心思想 可持久化线段树是利用函数式编程的思想,对记录

权值线段树&amp;&amp;可持久化线段树&amp;&amp;主席树

权值线段树 顾名思义,就是以权值为下标建立的线段树. 现在让我们来考虑考虑上面那句话的产生的三个小问题: 1. 如果说权值作为下标了,那这颗线段树里存什么呢? ----- 这颗线段树中, 记录每个值出现的次数 2.权值很大怎么办?数组空间不够啊 ----- 可以先离散化,再记录 3.那权值线段树到底是用来干嘛的呢? ----- 可以快速求出第k小值(其实主要还是为了主席树做铺垫啦) 那第k小值该怎么求呢??? 从树根依次往下 若当前值K大于左儿子的值,则将K-=左儿子的值,然后访问右儿子 若当前

小结:线段树 &amp; 主席树

概要: 就是用来维护区间信息,然后各种秀智商游戏. 应用: 优化dp.主席树等. 技巧及注意: size值的活用:主席树就是这样来的.支持区间加减,例题和模板:主席树,[BZOJ]1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树),[BZOJ]1901: Zju2112 Dynamic Rankings(区间第k小+树状数组套可持久化线段树(主席树)) 01(就是更新和不更新等这种对立操作)情况:我们就要在各个更新的操作中明白

UOJ#218. 【UNR #1】火车管理 线段树 主席树

原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ218.html 题解 如果我们可以知道每次弹出栈之后新的栈顶是什么,那么我们就可以在一棵区间覆盖.区间求和的线段树上完成这个问题. 于是本题的重点转到了如何求新的栈顶. 考虑用一个主席树维护一下每一个时刻每一个位置的栈顶元素的进栈时间,那么新的栈顶就是 当前位置栈顶的进栈时间-1 这时候的栈顶元素,然后这个东西也可以用我们维护的进栈时间来得到,所以我们只需要弄一个支持区间覆盖单点查询历史版本的主席树:这

4771: 七彩树 主席树

题意:给定一棵树  每个结点有一个颜色 然后又m个询问 询问:x d   问x的子树内不超过dep[x]+d 深度的子树结点一共有多少个颜色? 1.可以先将问题简化为问整个子树内有多少个不同的颜色  暴力解法树套树  但是可以用一个技巧来快速维护: 一个颜色一个颜色地处理  把所有相同颜色的点按照dfs序排序,每个点给自己的位置贡献1,相邻的两个点给lca贡献−1.然后只要区间内存在这种颜色,则其子树内的权值和必定为1.那么只需要这样子染好所有颜色之后询问子树和. 所以如果问题是这样的话只要一个

【BZOJ3439】Kpm的MC密码 trie树+主席树

Description 背景 想Kpm当年为了防止别人随便进入他的MC,给他的PC设了各种奇怪的密码和验证问题(不要问我他是怎么设的...),于是乎,他现在理所当然地忘记了密码,只能来解答那些神奇的身份验证问题了... 描述 Kpm当年设下的问题是这样的: 现在定义这么一个概念,如果字符串s是字符串c的一个后缀,那么我们称c是s的一个kpm串. 系统将随机生成n个由a…z组成的字符串,由1…n编号(s1,s2…,sn),然后将它们按序告诉你,接下来会给你n个数字,分别为k1…kn,对于每 一个k

bzoj 3545&amp;&amp;3551: [ONTAK2010]Peaks &amp;&amp;加强版 平衡树&amp;&amp;并查集合并树&amp;&amp;主席树

3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 635  Solved: 177[Submit][Status] Description 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走,现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1. I

[BZOJ4539][HNOI2016]树(主席树)

4539: [Hnoi2016]树 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 746  Solved: 292[Submit][Status][Discuss] Description 小A想做一棵很大的树,但是他手上的材料有限,只好用点小技巧了.开始,小A只有一棵结点数为N的树,结点的编号为1,2,-,N,其中结点1为根:我们称这颗树为模板树.小A决定通过这棵模板树来构建一颗大树.构建过程如下:(1)将模板树复制为初始的大树.(2)以下(2