第一次接触主席树,前队友说是个挺简单的东西,一直到今天才学…
主席树准确来讲是一棵可重用的线段树,仍然是解决区间问题,但是这里的询问会比普通的询问更多一维,比如询问\([L,R]\)的第k小(或者第k大),会发现一般的线段树很难处理——这时我们引入主席树。
主席树由很多个线段树组成的,但是这些线段树上有很多节点都是可以公用的。对于一棵线段树而言,他看上去就像是由很多个节点“拼起来”的。而对于每个节点,它可能会被多个线段树所利用,这样一来使得我们可以存储更多的信息,另一方面使得我们程序的空间不至于太大。
一般来讲我们做主席树习惯以前缀来建,前缀可以很好的维护一些具有可加性的性质,这个马上会看到。
HDU 2665
上面提到过的,这个题让我们求一个区间的第k小,我们考虑一个区间,如果我们要求这个区间的第k小,我们可以用先在线段树上插入每一个值,然后像排序树一样左右查找。但是现在有n个区间,于是我们想到了主席树。
我们对每一个前缀建线段树,这样对于每一个区间,我们可以通过前缀的可加性来减出当前区间的值,因为我们是对于每个前缀建树,所以查询的时候有点像在两颗线段树上查,然后减出当前的结果。
代码上写的很清晰。
Gym 101237A
这一题的意思是让我们求出一个区间里,没有出现过的最小正整数。
依旧是对前缀建树,只不过不用减了,我们记录每个值出现过的最右位置,在线段树上我们保存这个值的最小值,听起来有点绕口——意思是对于一个以\(1..i\)为根的线段树中,节点\([L,R]\)保存的是\([L,R]\)这些值中,某个值出现位置的最小位置(如果有值未出现,那么这个值就是0)。
这样我们就可以查询左区间的值来和L作比较,如果左区间的值小于l,说明左区间有值未在\([L,R]\)中出现,这样我们就可以像排序树一样左右查询了。