前言

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

应用

应用的比较多的是查询区间的第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]中的排名之类的。

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

Ze_Min Tree 主席树的更多相关文章

  1. 【BZOJ-2588】Count on a tree 主席树 + 倍增

    2588: Spoj 10628. Count on a tree Time Limit: 12 Sec  Memory Limit: 128 MBSubmit: 3749  Solved: 873[ ...

  2. spoj cot: Count on a tree 主席树

    10628. Count on a tree Problem code: COT You are given a tree with N nodes.The tree nodes are number ...

  3. Bzoj 2588: Spoj 10628. Count on a tree 主席树,离散化,可持久,倍增LCA

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2588 2588: Spoj 10628. Count on a tree Time Limit ...

  4. 洛谷P2633 Count on a tree(主席树上树)

    题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始为0,即第一个 ...

  5. 【BZOJ2588】Spoj 10628. Count on a tree 主席树+LCA

    [BZOJ2588]Spoj 10628. Count on a tree Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lasta ...

  6. SPOJ Count on a tree(主席树+LCA)

    一.题目 COT - Count on a tree You are given a tree with N nodes. The tree nodes are numbered from 1 to  ...

  7. 洛谷P2633/bzoj2588 Count on a tree (主席树)

    洛谷P2633/bzoj2588 Count on a tree 题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K ...

  8. 洛谷 P2633 Count on a tree 主席树

    在一棵树上,我们要求点 $(u,v)$ 之间路径的第$k$大数. 对于点 $i$  ,建立 $i$  到根节点的一棵前缀主席树. 简单容斥后不难得出结果为$sumv[u]+sumv[v]−sumv[l ...

  9. [bzoj2588][count on a tree] (主席树+lca)

    Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始 ...

随机推荐

  1. 手把手教你通过Eclipse工程配置调用JNI完全攻略

    本文地址:http://www.cnblogs.com/wavky/p/JNI.html 当你找到并鬼使神差地打开这个博文的时候,我敢肯定你已经知道什么是JNI,基本概念就不粘贴了. 百度出来的JNI ...

  2. JQuery用鼠标选文字来发新浪微博

    最近注意到新浪博客有个小功能,就是当鼠标选中一段文字时会浮现一个小图片,点击这个图片可以把选中内容发送到新浪微博,一时兴起昨晚就写了一个Demo玩了一下,代码超简单,没优化,有兴趣的朋友可以自己改进. ...

  3. POJ3020:Antenna Placement(二分图匹配)

    Antnna Placement Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 11093   Accepted: 5459 ...

  4. php设定错误和异常处理可使用的函数

    1.register_shutdown_function 使用场景:当我们的脚本执行完成或意外死掉导致PHP执行即将关闭时,这个函数会被调用. 函数介绍: void register_shutdown ...

  5. vue2.0基础知识,及webpack中vue的使用

    ## 基础指令 ## [v-cloak]{         Display:none;     }     <p v-cloak>xx{{msg}}xx</p> //解决闪烁问 ...

  6. Java并发(4)- synchronized与CAS

    引言 上一篇文章中我们说过,volatile通过lock指令保证了可见性.有序性以及"部分"原子性.但在大部分并发问题中,都需要保证操作的原子性,volatile并不具有该功能,这 ...

  7. 随机洗牌算法Knuth Shuffle和错排公式

    Knuth随机洗牌算法:譬如现在有54张牌,如何洗牌才能保证随机性.可以这么考虑,从最末尾一张牌开始洗,对于每一张牌,编号在该牌前面的牌中任意一张选一张和当前牌进行交换,直至洗到第一张牌为止.参考代码 ...

  8. 「6月雅礼集训 2017 Day1」说无可说

    [题目大意] 给出n个字符串,求有多少组字符串之间编辑距离为1~8. n<=200,∑|S| <= 10^6 [题解] 首先找编辑距离有一个n^2的dp,由于发现只找小于等于8的,所以搜旁 ...

  9. 【51NOD】1717 好数

    [算法]数学 [题意]a数组初始为0,t=1~n,每次01翻转t的倍数,最终为0的数字定义为好数,求好数个数 [题解]一个数字为好数的条件是翻转偶数次,也即一个数是好数当且仅当有偶数个因子时. 因子都 ...

  10. Linux服务器中毒事件(libudev.so)

    今天机房管理人员反馈公司的某台服务器在防火墙上的连接数超限,登陆服务器时发现非常卡顿,远程登录后查看,CPU持续100%,且有一长度为10的随机字符串进程,kill掉,会重新生成另外长度为10的字符串 ...