(施工中orz)
20171223:更新一些关于线段树的基础用法,以及简单的zkw线段树、权值线段树,动态开点线段树,线段树的标记永久化,主席树,可持久化线段树,可持久化线段树的标记永久化(施工中)
这几天学了学各种姿势的线段树
什么是线段树呢?请先思考这样一个问题:
给定一个长度为n的数组,有m次操作,每次操作有如下几种可能:
1、给ai加上v
2、给a[L,R]上的每个数加上v
3、求区间[L,R]上a的最大/小值
4、求区间[L,R]上∑(i∈[L,R])ai
5、查询ai的值
当然,这些操作都可以只用一个数组a进行模拟来完成
现在我们分析一下复杂度:对于操作1、5来说,每次的时间复杂度为O(1),因为只需要修改或输出数组a[i]的值就好了,但是对于操作2、3、4来说,每次的时间复杂度为O(区间长度)
在这种情况下,,理论最差情况下的时间复杂度是O(mn)的
这样的方法显然对于十万及以上级别的数据非常棘手
因此我们需要一种可以处理较大数据量的数据结构
这时候树状数组 线段树就来了
既然区间问题不好处理,那么我们是否能够想到一种查找区间效率较高的算法呢?
仔细想一下:对于两个区间[l,m],[m+1,r],l<m<r来说,这两个区间我们可以合成为一个区间,即区间[l,r]
那么我们也可以说,区间[l.r]包含了区间[l,m]和[m+1,r],同时,[l,m],[m+1,r]这两个区间内又包含了其它的区间
于是我们就可以将区间[1,n]进行不断的细分,最后所有区间的长度都为1
显然我们可以用树来表示区间之间的从属关系。
为了方便,我们一般采用二叉树的形式来存储区间,且区间[l,r]的2个儿子节点之间的分界线恰为[l,r]的中点
也就是说,我们通过树,用二分的方法,间接地将区间进行了细分
这就是区间树。可以证明,二叉区间树的深度为logn(本文中所有的log均为log2)
那么,在这棵区间树上,如果要查询区间[l,r]的值,那么我们可以对这个区间进行细分,将其化为区间树上的一些点所代表的区间的集合
因为在区间树上,我们最终将整个区间都划分为了长度为1的区间,那么对于任何区间,都可以在区间树上以点的集合的形式进行表示
因为当我们在区间树上找到一个区间属于当前要操作的区间时,其从属的区间没有必要进行查找,那么显然在区间树上查询一个区间的值的时间复杂度是log级别的
因此单点操作的时间复杂度也是log级别的
这样的话我们就能将单点操作和区间操作的时间复杂度进行均摊,达到log级别
可以发现,区间树的时间复杂度是非常优秀的
而大部分情况下线段树就是在二叉区间树上进行操作的数据结构
那么我们考虑一下以上几种操作的具体事项:
对于单点操作来说,我们直接递归到对应的叶子结点,然后修改即可
对于区间修改来说,我们需要通过递归找到所有对应的区间(包括对应区间的子区间),然后进行修改
对于区间查询来说,只需要找到对应的区间即可
(施工中orz)