介绍:
线段树是一棵二叉搜索树,思想与分治很想,把一段区间平分平分再平分,平分到不能平分为止,可以进行方便的区间修改和区间查询,当然,树状数组能做的单点修改、单点查询,线段树也可以更好地实现,总之,线段树是树状数组的升级版,此外,线段树能做的平衡树也能做,但平衡树码量太大,考场上一般写不出来反正我是写不出来,就是会写也不会用,其次有些特殊的线段树如权值线段树也可以水过平衡树,可见,线段树的应用还是十分广泛的。
图片来自 OI-WikiOI-Wiki
建树:
一般的线段树都是递归建树,当然,也有一种不用递归建树的线段树,即非递归线段树——zkw 线段树
当然,zkw 线段树并不是我们现在的主角,我们讲一下递归建树的操作
首先,可以设置三个方便的宏定义偷懒
typedef long long ll;
#define ls (p << 1)
#define rs (p << 1 | 1)
#define mid ((l + r) >> 1)
// ls 左儿子 rs 右儿子 mid 中间值
因为线段树是棵二叉树,pp 是当前节点的编号,所以一个结点的左儿子和右儿子可以表示为p×2p×2 和 p×2+1p×2+1,用位运算速度更快,但是要注意运算符之间的优先级,不熟悉的那就勤加括号吧反正加几个括号基本没影响,还不容易出错,不加白不加,至于这个 midmid,纯粹是图省事
刚才我们也讲了左右儿子的表示方法,所以我们的空间要开到原来的四倍,当然,线段树还有一种结构体存法,那个存法开两倍空间即可,但要维护的东西不少,所以也没剩多少空间,而且操作起来很麻烦,不如用这种左右儿子表示法来存储
ll val[N << 2];
如图,这是一颗线段树,每一个节点都代表着一个区间的信息,如 11 号节点代表着区间 [1,5][1,5]的和,44 代表着区间 [1,2][1,2] 的和,我们发现它是从中间值平分开的,这里就是分治的思