浅谈主席树
可持久化数据结构
可持久化数据结构就是支持历史询问的数据结构。比如一共有\(5411\)次操作,我问你第\(251\)次操作之后这个数据结构长啥样,你能在约束的时间空间内回答出来就算支持了可持久化,否则就不算。一种很××的做法就是每次更改构之后我都把它保存下来,然后你问哪次我就去哪次里面找就是了。但是这显然不优秀。然后前辈们发现,每次修改只会让该数据结构某部分与之前不同,那就只需要记录这不同的部分就行了。
主席树
主席树就是可持久化线段树,是函数式线段树(别问我函数式是啥意思我也不知道),如果你还不会线段树请出门左转。对于每次修改,我们只需要把该次修改所改动的结点新建一遍,其余的结点跟上一历史版本一样即可。如果每次都是单点修改的话,那么就只会增加\(logn\)个新结点。由于动态开点的原因,左右儿子就不能通过完全二叉树找左右儿子那种方式来找了,必须要记录自己的左右儿子编号。
假设当前主席树是前\(i-1\)次操作之后的成果,根为\(root[i-1]\),一共有\(tot\)个结点,现在要进行第\(i\)次操作。
1、令\(root[i]\)=\(++tot\),\(p=root[i-1],q=root[i]\)。
2、如果要修改的点在左儿子子树内,并且\(p\)不为\(0\),那么就令\(q\)的右儿子等于\(p\)的右儿子。然后新建一个点给\(q\)当左儿子,\(q=ls[q],p=ls[p]\)。右儿子以此类推
3、如果到这条修改路径的底端则回溯更新结点信息,否则重复第二步。
这样子建出来的主席树,从\(root[i]\)开始访问,就可以访问到\(1\)~\(i\)次操作之后的数据结构了。因为每次只会新建\(logn\)个结点,所以空间复杂度只会随着修改次数缓慢增长,而线段树作为主席树的本质内部结构,时间复杂度也能过关,就这样,主席树就建好了。但是如果要区间修改的话,还需要一些骚操作,这里就不讲了。