博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Ze_Min Tree 主席树
阅读量:5139 次
发布时间:2019-06-13

本文共 1919 字,大约阅读时间需要 6 分钟。

前言

主席树,也叫可持久化线段树,所以他的本质是颗线段树,而可持久化指的是这颗线段树可以访问过去某个时刻线段树上的信息。

应用

应用的比较多的是查询区间的第k大值(因为其他的数据结构不好做)。

实现

下面来讲讲如何用主席树实现区间第k大。

这里的主席树是一颗权值线段树,即线段树上的一个点[l,r]表示值在[l,r]中的数有多少个。
例如:1 3 5 7 9
那么data[1,10]=5,data[1,5]=3;
那么如果给你一颗权值线段树,让你找第k大,应该不是道难题,只需要像二叉查找树一样,如果左儿子的数少于k个,我就到右儿子去找第k-lsize大,否则就在左儿子找。
但是这个权值线段树局限性很大,只能针对一个询问,那么我们如何能把每个区间的权值线段树都建出来呢,
用到前缀和的思想,因为所有的权值线段树都是同构的,而且出现的次数也是有可减性的,
所以我们拿第r个时刻的权值线段树减掉第l-1个时刻的权值线段树,就可以得到l..r这个区间对应的权值线段树了,这里的减指每个对应节点的值相减。
然后可持久化线段树一下就行了啊,等等,怎么可持久化,如果把每个时间点的权值线段树记下来,空间是$ n^2\(级别的,不能接受。 但是我们注意到一点,每个时间点,只会插入一个数,所以i时间的权值线段树上只有\) log\(个节点是和i-1时间点的不同的,所以我们只要记录一下这\) log\(个节点就行了,空间复杂度变为\) n*log_2$的了。

代码

原理应该是清楚的,但这个东西好像听听就很难写啊,实际上并不是这样的,(虽然本蒟蒻第一次学也以为很难写,还好有tututu大佬教)

两个核心操作,insert和query,可以看到代码其实很短,结合注释来讲解

void insert(int &x, int pre, int l, int r, int k){//表示我当前为x号节点,我上个时刻对应节点为pre,我的区间为l..r,我插入的值为k    x = ++tot;//每次要新开一个节点,动态开点线段树    size[x] = size[pre] + 1;//我的值是我pre节点的值加一,好理解    lson[x] = lson[pre];    rson[x] = rson[pre];    if (l == r) return;    int mid = (l+r)>>1;//二分,看我现在这个权值属于左边还是右边    if (k <= mid){        insert(lson[x], lson[pre], l, mid, k);    }    else insert(rson[x], rson[pre], mid+1, r, k);}//需要注意的是这个x是个变参,所以我递归下一层的时候自动会把lson或者rson改掉,大大降低了代码难度int query(int x, int y, int l, int r, int k){//这里的x和y都是节点编号,[l,r]就是现在这个区间,第k大,查找的过程与二叉查找树类似    if (l == r) return l;    int mid = (l+r)>>1, ll = size[lson[y]]-size[lson[x]];    if (ll >= k) return query(lson[x], lson[y], l, mid, k);    else return query(rson[x], rson[y], mid+1, r, k-ll);}//下面把主程序中如何使用也贴出来,还是有很多细节很容易写错的for (int i = 1; i <= n; i++){    insert(root[i], root[i-1], 0, cnt, w[i]);    //注意insert里都是节点编号,所以这里外面要套root}while (m--){    int x, y, k;    read(x), read(y), read(k);    printf("%d\n", t[query(root[x-1], root[y], 0, cnt, k)]);    //查询也是,外面也要套root,然后y-(x-1)才是[x,y]}//用之前离散一下就ok了

同样,主席树的别的操作也就是在权值线段树上搞,比如求一个数在[l,r]中的排名之类的。

其实主席树也没有那么的难,希望这篇博客能给大家带来帮助。

转载于:https://www.cnblogs.com/Michael-Li/p/8742874.html

你可能感兴趣的文章
软件人机界面设计
查看>>
requests模块下载视频 显示进度和网速
查看>>
Elasticsearch 学习之 分片未分配原因
查看>>
.NET项目从CI到CD-Jenkins_Pipeline的应用
查看>>
E2.(b)
查看>>
ini 解析库,config 解析库不完全列表
查看>>
awk常见操作整理(更新)
查看>>
javaEE项目实践——学生信息管理系统
查看>>
E. Black Box
查看>>
( ̄▽ ̄") 没钱了
查看>>
模拟退火算法实现代码
查看>>
代码中动态添加Button的点击事件
查看>>
【luogu P2071 座位安排】 题解
查看>>
两道FFT的应用题
查看>>
STL next_permutation 全排列
查看>>
IntentActivity的用法(activity的五态之外)
查看>>
小米手机不断自己重启问题解决
查看>>
软件测试为何我会首选Python
查看>>
python读取指定字节长度的文本
查看>>
二元线性回归
查看>>