学习主席树,在网上搜了很多教程(都好简短啊,直接就是几行字就上代码,看不懂啊有木有~~),最后才很艰难的学会了最基础的部分。下面就是我在学习的过程中的产生的疑惑和解决的办法。
学习主席树需要的前置技能:线段树。
参考资料
1. B站上的视频讲解(话说B站真的啥都有啊)
https://www.bilibili.com/video/av4619406/?p=1
2.参考博客
https://blog.csdn.net/metalseed/article/details/8045038
主席树是啥??
话说主席树这个名字的来历还是挺好玩的 ,发明它的人叫黄嘉泰,名字的首字母呢就是(HJT),和我们的某位主席的名字简写是一样的,所以就有了这个名字。
言归正传,那么,主席树到底是啥,主席树是一种可持久化数据结构(可持久化线段树)(也就是可以查询历史版本的线段树),也叫函数式线段树。具体点说就是你对当前状态的线段树进行了很多次更新,然后在后面的过程中,你还可以找到这个更新之前的版本的线段树。再说通俗点就是每一次更新,都会把旧的线段树存起来,这样以后用的时候就可以直接找啦。显然直接创建一堆线段树是显然会爆内存的。但是呢我们发现,如果更新了一个点,那么只有一条线上的节点会被更新,也就是说,我们记录下一个版本的线段树的时候,可以共用前一个版本线段树的大部分节点,这样就可以节省内存啦。(巧妙啊)(所以也可以说主席树是一些相互之间联系密切的线段树的集合)
如下图,修改红圈内的点,只会影响这一条线上的点。(因为线段树每一个节点所维护的信息都只是由左右子树的信息合并得到的)
对于一般的线段树来说,如果父节点的编号是 i ,那么他的两个子节点的编号分别为 2 * i(左), 2 * i + 1(右),但是主席树在这一点则有别于一般的线段树,每一个父节点,他的两个子节点的编号则不一定满足这个关系(因为我们上面所说的节点共用0.0)(示意图如下)。
红色的点是更新是修改的点,对于这些点,我们没有办法用以前的节点(因为是新修改的节点),所以我们只能把这些节点新建一份(蓝色的点),但是,剩余的其他节点的信息都是相同的,我们就没有必要再去创建新的节点了,直接把儿子节点的下标指向前一个版本对应的节点的下标即可(如图中的红色虚线)。