zkw线段树学习笔记

  1. zkw线段树是什么
    zkw线段树,顾名思义,是一种线段树。
    他是线段树的非递归形式,虽然没递归版线段树那么通用,但它的常数与码量吊打递归版线段树。

  2. zkw线段树的构造
    说了那么多,不会写也没用啊。
    递归式线段树自上往下遍历,即从根节点到叶节点,再回到根节点。
    zkw则正好相反,直接从叶节点到根节点。
    这就要我们记录下每一个叶节点的位置。
    但在一般情况下不太好记录。
    image
    如图,每个叶节点的位置不太规律。
    但有一种特殊情况。
    image
    也就是叶节点的个数是2的次幂的时候,线段树是一棵满二叉树。
    zkw线段树的方法就是在原数列后面补0,使其变为满二叉树的形态。
    这样叶子节点就很好找。
    构建代码如下:

    int M,tr[N<<2],n;//n为原始数据个数,tr为线段树,M为填充后数据个数
    void build(){
    	for(M=1;M<=n;M<<=1);
    	//根据需要自行补充
    }
    
  3. zkw线段树单点操作
    可以发现,\(M\)既是填充后数据个数,也是填充后第一个数据的编号,将其留空(有用)
    因此,原数据第\(i\)个对应的叶节点编号为\(i+M\)
    单点修改就在叶节点直接修改,向上pushup

    //单点增加代码
    void modify(int pos,int x){
    	pos+=M;tr[pos]+=x;pos>>=1;
    	while(pos){
    		tr[pos]=tr[pos<<1]+tr[pos<<1|1];
    	}
    }
    

    单点查询更加简单。相信不用说了。

  4. zkw线段树区间操作
    区间查询就当然不能一个一个查了这和暴力有什么区别
    正解:设两个指针\(s\),\(t\),初始时分别指向\(l-1\),\(r+1\)的位置(0的位置派上用场了)。
    然后同步向上跳,父亲节点相同(即成为兄弟时)时停止向上跳。
    如果\(s\)为父亲的左儿子,那将\(s\)兄弟的数据合并。
    因为\(s\)肯定包含\(l-1\),且\(t\)不为\(s\)兄弟,所以\(s\)兄弟数据在\(l,r\)范围内。
    同理,如果\(t\)为父亲的右儿子,那将\(t\)兄弟的数据合并。

posted @ 2022-05-08 12:49  Andy__Lin  阅读(84)  评论(0)    收藏  举报