1.poj 2104 查询区间第k小。
主席树其实相当于建立了n棵线段树,第i棵线段树是根据区间【1,i】按值建立的。对于每一棵线段树我们记录它对应的区间每个数出现的次数,所以首先要对所有的数离散化。
先考虑最简单的情况,只查询【1,n】的第k小,对于【1,n】我们按值建立一棵线段树,对于a[i]我们在位置a[i]上加1。查询第k小那么先看左子区间出现了多少个数cnt,假设左区间出现的数cnt>=k,那么直接递归到左区间查询(因为是按值建立的,左区间的数肯定小于右区间),否则递归到右区间查询第k-cnt小(左区间已经有了最小的cnt个数了)
对于任意区间查询【l,r】,我们只需要比较第l-1棵线段树和第r棵线段树,【l,r】之间的数就是第r棵线段树相比于第l-1棵多出来的数。只需要对比两颗树同一个节点,对比到哪个数为止第r棵比第l-1棵刚好多出k个数。(先比较左区间cntr-cntl,cntr-cntl>=k,则递归到左区间,否则递归查询右区间k-cntr-cntl)
主席树就相当于n棵线段树,但是对比建立在【1,i】的线段树和【1,i+1】的线段树,只多出了一个值,也就是相当于单点更新他们之间只有logn个节点是不同的,所以可以将【1,i+1】的一些节点指针指向前一棵的共同部分。每次新增的空间只需要logn。
代码是根据kuangbin模板抄的。。。。
#include
#include
#include
#include
#include
#include
#include
#include
2.hdu 4417 区间查询<=H的数有多少个
查询【l,r】区间只需要将第r棵线段树【0,H】区间的总数减去第l-1棵的就行了。
#include
#include
#include
#include
#include
#include
#include
#include
3.hdu 4348 可持久化线段树,区间更新,不下放的懒惰标记(空间优化)
主席树其实就是可持久化线段树。可持久化就是每次修改操作,尽量用新节点表示而不是直接修改原来的点,这样所有的历史版本都得以保留。
主要麻烦的就是区间更新。区间更新对于完全覆盖的区间要用lazy标记。但是每次lazy下放的时候两个子区间都发生修改需要创造两个新的节点,这样到最后下放到最后一层相当于消耗了O(n)个新节点,空间会爆。
这道题用的空间优化就是不下放标记。标记就打在那个区间节点上,而查询的时候,往下递归时遇到标记就累加,最后把标记的影响加到总答案里。这样就不需要创造那么多新节点了。
#include
#include
#include
#include
#include
#include
#include
#include