关于线段树的一点小笔记
关于线段树的一点小笔记
线段树:
- 
线段树是一棵天生支持单点修改、查询和区间查询的一棵完全二叉树,其单点修改、查询和区间修改的时间复杂度均为\(\Theta(\lg n)\) 
- 
在区间修改时,如果我们暴力的修改并维护区间信息,那么时间复杂度将会退化为\(\Theta(n)\),这显然不是我们想要的结果,但是,我们可以通过引用懒标记(lazytag)的思想来进行区间修改,具体的操作是这样的: - 
如果当前所在节点所表示的区间被目标区间所覆盖,则在当前节点上打上懒标记 
- 
如果当前所在节点所表示的区间没有被目标区间所覆盖,先下传懒标记,然后在递归到自区间进行修改 
 
- 
- 
通过懒标记的方式,我们可以将不支持区间修改的线段树改为为支持区间修改操作,这样的时间的复杂度也是\(\Theta(\lg n)\),在所能接受的范围之内 
- 
在维护区间或在处理多个懒标记的过程中,我们应当正确处理区间之间和懒标记之间的关系,例如P1471 方差这道题,如果先处理区间和,在处理区间平方和的话,就会导致区间平方和的错误,再比如P7453 大魔法师这一道题,如果针对\(a,b,c\)的懒标记没有正确的处理,就会导致时间复杂度退化为\(O(n)\)的问题 
- 
线段树并不是万能的,当需要维护的信息满足以下几个条件时,我们才可以使用线段树: - 
所维护的信息本身可以快速的合并 
- 
为了达成条件,需要维护一些其它的信息 
 
- 
- 
并不是所有的线段树都支持区间修改,只有当懒标记的信息可以合并时,区间修改才有意义 
- 
平衡树已经死了:当平衡树所涉及 的操作没有区间翻转时,我们就可以使用线段树来代替平衡树维护,例如大名鼎鼎的平衡树模板题P3369【模板】普通平衡树,就完全可以用线段树来进行操作,既简单,又方便。
线段树的衍生数据结构:
一、树状数组:
- 
树状数组 \(\subset\) 线段树: - 树状数组可以看做是没有右儿子的线段树,如下图所示(图中蓝色虚线代表不存在的节点):
  - 
树状数组天生支持单点修改和单点查询 
- 
在源数据可以差分的情况下,树状数组也支持区间修改和区间查询 
 
二、二进制字典树(目前还不会):
一些线段树的骚操作和一些奇怪的线段树:
一、动态开点:
- 
在平常建立线段树的时候,我们经常把节点\(p\)的左右儿子标号为\(p*2\)和\(p*2+1\),但是在最坏情况下这样建树就需要至少\(4n\)的节点才能保证访问时数组不越界(当然,用指针的同学除外),于是这时候,我们就需要一个骚操作来节省空间,这个骚操作也就是动态开点,他可以使得线段树的最坏空间复杂度为\(O(2n+1)\) 
- 
动态开点的核心思想是:当你需要一个叶子节点时,若该叶子节点不存在,则把它和它的所在区间全部建立出来 
- 
动态开点模板板子: 
struct SegmentTree{
    int data;//指需要维护的数据,并没有具体的含义
    int lc,rc;
};
SegmentTree seg[MAX_SIZE];
int tot=0;//已添加的节点个数
int build(){//建立一个新的节点
    ++tot;
    seg[tot].lc = seg[tot].rc = seg[tot].data = 0;
    return tot;
}
void insert(int p,int l,int r,int val,int data){//插入一个节点
    //p:表示当前所在节点下标
    //l:表示当前节点所表示区间的左端点
    //r:表示当期节点所表示区间的右端点
    //val:表示目标位置
    //data:表示目标位置要填的数据
    if(l==r){
        seg[p].data = data;
        return ;
    }
    int mid = (l+r)>>1;
    if(val<=mid){
        if(!seg[p].lc)
            seg[p].lc = build();
        insert(seg[p].lc,l,mid,val);
    }
    else{
        if(!seg[p].rc)
            seg[p].rc = build();
        insert(seg[p].rc,mid+1,r,val);
    }
    pushup(p);
}
二、势能线段树:
- 
在线段树的性质中,我们讲到了使用线段树的两个限制条件,然而在某些操作下,我们可以通过线段树对序列进行维护,这些操作通常有一个隐含的“上界”,就相当于有一个“势能”,当操作达到或者超过了这个上线,也就是它的势能小于等于0时,相应的操作就会退化失效,从而可以用线段树维护,我们也把这样的线段树叫做“势能线段树” 
- 
在势能线段树中,我们要考虑以下几个要点: - 
现在的势能是多少 
- 
势能为零时如何操作 
- 
如果还有区间势能不为零,则暴力递归修改下去 
 
- 
- 
时间复杂度均摊下来通常为\(\Theta(\lg n)\) 
三、值域线段树:
- 是平衡树的主要竞争对手之一,其主要思想为,把每一个节点看做一个桶,这样的话区间\([l,r]\)就表示在区间内部的数值,然后节点总维护的是这个桶中

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号