可持久化数据结构介绍

可持久化数据结构是保存数据结构修改的每一个历史版本,新版本与旧版本相比,修改了某个区域,但是大多数的区域是没有改变的,

所以可以将新版本相对于旧版本未修改的区域指向旧版本的该区域,这样就节省了大量的空间,使得可持久化数据结构的实现成为了可能。

如下图,就是可持久化链表

插入前

插入后

尽可能利用历史版本和当前版本的相同区域来减少空间的开销。

而主席树(可持久化线段树)的原理同样是这样。

有n个数字,  我们将其离散化,那么总有[1,n]个值,如果建一棵线段树,每个结点维护子树中插入的值的个数。 求总区间第k大

那么如果k>=左子树中值的个数,那么就去左子树中找,   否则就去右子树中找第(k-左子树值的个数)大值。

如果要求区间[l,r]第k大,那么就要维护n棵线段树

每棵线段树维护的都是[1,n]值出现的个数, 所以每棵线段树的形态结构是相同的

第i棵线段树的根为T[i] , 维护的是前i个数字离散化后出现的次数。

那么主席树有两个性质

线段树的每个结点,保存的都是这个区间含有的数字的个数

主席树的每个结点,也就是每棵线段树的大小和形态是一样的,也就是主席树的每个结点(线段树与线段树之间)是可以相互进行加减运算的

假设要求区间[l,r]的第k大, T[r]左子树值的个数 - T[l-1]左子树值的个数大于>=k  ,  那么就说明  区间[l,r]的数离散化后有大于n个数插入了左子树,

所以应该去左子树去找第k个大, 反之,去右子树找 第(k- (T[r]左子树值的个数 - T[l-1]左子树值的个数) )大。

至于主席树的构建, 第T[i+1]棵树相比第T[i]棵树,多插入了一个数字, 也就相当于修改了一条链。

如果第a[i+1]个数插入了T[i+1]的左子树, 那么T[i+1]的右子树和T[i]的右子树是一样的, 所以可以直接指向T[i]的右子树, 然后子树的构造也是这个道理。

poj2104

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
/*
可持久化线段树,函数式线段树,主席树
动态第k大 线段树维护的是值出现的次数
*/
const int N = ;
int a[N], t[N];
//T[i] 保存第i棵线段树的根
int T[N], lson[N * ], rson[N * ], c[N * ];
int n, m, q, tot;
int build(int l, int r)
{
int root = tot++;
c[root] = ;
if (l != r)
{
int mid = (l + r) >> ;
lson[root] = build(l, mid);
rson[root] = build(mid + , r);
}
return root;
} //root是第i棵线段树的根, 现在构造第i+1棵树, pos是a[i+1]所要插入的位置
int update(int root, int pos)
{
int newRoot = tot++, tmp = newRoot;
int l = , r = m;
c[newRoot] = c[root] + ;
while (l < r)
{
int mid = (l + r) >> ;
if (pos <= mid)//要插入的位置位于左子树, 要么右子树可以指向第i棵线段树的右子树
{
r = mid;
lson[newRoot] = tot++;
rson[newRoot] = rson[root];
newRoot = lson[newRoot];//继续构造左子树
root = lson[root];
}
else
{
l = mid + ;
lson[newRoot] = lson[root];
rson[newRoot] = tot++;
newRoot = rson[newRoot];
root = rson[root];
}
c[newRoot] = c[root] + ;//该子树保存的值的个数+1
}
return tmp;
} int hs(int x)
{
return lower_bound(t + , t + m + , x) - t;
} int query(int leftroot, int rightroot, int k)
{
int l = , r = m;
while (l < r)
{
int mid = (l + r) >> ;
if (c[lson[rightroot]] - c[lson[leftroot]] >= k)
{
r = mid;
rightroot = lson[rightroot];
leftroot = lson[leftroot];
}
else
{
l = mid + ;
k -= c[lson[rightroot]] - c[lson[leftroot]];
rightroot = rson[rightroot];
leftroot = rson[leftroot]; }
}
return l;
}
int main()
{
while (scanf("%d%d", &n, &q) != EOF)
{
tot = ;
for (int i = ; i <= n; ++i)
{
scanf("%d", &a[i]);
t[i] = a[i];
}
sort(t + , t + n + );
m = unique(t + , t + n + ) - t - ;
T[] = build(, m);
for (int i = ; i <= n; ++i)
{
int pos = hs(a[i]);
//第i棵线段树由第i-1棵线段树修改而来
T[i] = update(T[i - ], pos);
}
while (q--)
{
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
printf("%d\n", t[query(T[l - ], T[r], k)]);
} }
return ;
}

主席树(可持久化线段树) 静态第k大的更多相关文章

  1. 主席树||可持久化线段树+离散化 || 莫队+分块 ||BZOJ 3585: mex || Luogu P4137 Rmq Problem / mex

    题面:Rmq Problem / mex 题解: 先离散化,然后插一堆空白,大体就是如果(对于以a.data<b.data排序后的A)A[i-1].data+1!=A[i].data,则插一个空 ...

  2. [BZOJ 4771]七彩树(可持久化线段树+树上差分)

    [BZOJ 4771]七彩树(可持久化线段树+树上差分) 题面 给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点.每个节点都被染上了某一种颜色,其中第i个节点的颜色为c[i].如果c[i] ...

  3. 权值线段树&&可持久化线段树&&主席树

    权值线段树 顾名思义,就是以权值为下标建立的线段树. 现在让我们来考虑考虑上面那句话的产生的三个小问题: 1. 如果说权值作为下标了,那这颗线段树里存什么呢? ----- 这颗线段树中, 记录每个值出 ...

  4. 归并树 划分树 可持久化线段树(主席树) 入门题 hdu 2665

    如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k ...

  5. 主席树[可持久化线段树](hdu 2665 Kth number、SP 10628 Count on a tree、ZOJ 2112 Dynamic Rankings、codeforces 813E Army Creation、codeforces960F:Pathwalks )

    在今天三黑(恶意评分刷上去的那种)两紫的智推中,突然出现了P3834 [模板]可持久化线段树 1(主席树)就突然有了不详的预感2333 果然...然后我gg了!被大佬虐了! hdu 2665 Kth ...

  6. BZOJ4771七彩树——可持久化线段树+set+树链的并+LCA

    给定一棵n个点的有根树,编号依次为1到n,其中1号点是根节点.每个节点都被染上了某一种颜色,其中第i个节 点的颜色为c[i].如果c[i]=c[j],那么我们认为点i和点j拥有相同的颜色.定义dept ...

  7. [POJ2104/HDU2665]Kth Number-主席树-可持久化线段树

    Problem Kth Number Solution 裸的主席树,模板题.但是求k大的时候需要非常注意,很多容易写错的地方.卡了好久.写到最后还给我来个卡空间. 具体做法参见主席树论文<可持久 ...

  8. BZOJ.4771.七彩树(可持久化线段树)

    BZOJ 考虑没有深度限制,对整棵子树询问怎么做. 对于同种颜色中DFS序相邻的两个点\(u,v\),在\(dfn[u],dfn[v]\)处分别\(+1\),\(dfn[LCA(u,v)]\)处\(- ...

  9. BZOJ 3483 SGU505 Prefixes and suffixes(字典树+可持久化线段树)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3483 [题目大意] 给出一些串,同时给出m对前缀后缀,询问有多少串满足给出的前缀后缀模 ...

  10. BZOJ 3439 Kpm的MCpassword Trie树+可持久化线段树

    题目大意:给定n个字符串,对于每一个字符串求以这个字符串为后缀的字符串中第k小的编号 首先将字符串反转 那么就变成了对于每一个字符串求以这个字符串为前缀的字符串中第k小的编号 然后考虑对字符串排序 那 ...

随机推荐

  1. POJ 1273 Drainage Ditches(网络流,最大流)

    Description Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover ...

  2. 利用开源HTML5引擎lufylegend.js结合javascript实现的五子棋人机对弈

    前言     本文主要介绍利用开源引擎 lufylegend.js开发基于Html5的游戏--五子棋,主要叙述其详细开发过程. 游戏规则 玩过五子棋的都应该知道五子棋的规则,这里就简单介绍其规则. 1 ...

  3. ExtJs4 笔记(1) ExtJs大比拼JQuery:Dom文档操作

    现在主流的JS框架要数ExtJs和JQuery应用的比较广泛.JQuery属于轻量级的,一般做网站应用比较常见,可见块头小的优势.ExtJs比较庞大,它除了对基本的JS语法和HTML DOM操作方式的 ...

  4. Effective C++:规定12:不要忘了复制的对象时,它的每一个组成部分

    (一个) 继承制度的声明: class Date {...}; class Customer { public: ... private: string name; Date lastTransact ...

  5. json介绍及简单示例

    JSON的定义: 一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性.业内主流技术为其提供了完整的解决方案(有点类似于正则表达式 ,获得了当今大部分语言的支持),从而可以在不同平台间进行数据 ...

  6. Java DatagramSocket(UDP)要注意的问题

    因为byte数组的大小问题,在网络发送过程中.可能包括多余的空格,若接收时要对数据进行比較,最好先将数据做下处理,处理掉多余的空格.

  7. 《转》python 网络编程

    原地址:http://blog.163.com/benben_long/blog/static/19945824320121225918434/ 网络客户端: 1. 理解socket: socket是 ...

  8. [Android学习笔记]自定义控件的使用

    自定义控件时,最好抽象得彻底,并且编写需严谨,因为可能程序中多处都会引用到它,或者提供给团队中的其他人使用. 其一般步骤为: 1.创建控件的类文件,定义其功能逻辑.一般继承自现有控件或者View2.在 ...

  9. 求第i个小的元素 时间复杂度O(n)

    #include<iostream> //求第i个小的元素 时间复杂度O(n) #include<cstdlib> #include<ctime> using na ...

  10. 13年7月13日CF练习 Codeforces Round #147 (Div. 2)

    这场div2可以说是我见过的比较水的一场吧.基本都是一眼题. 比赛地址http://acm.bnu.edu.cn/bnuoj/contest_show.php?cid=1836 题号是237A-237 ...