线段树1

Posted tply

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树1相关的知识,希望对你有一定的参考价值。

线段树的基本操作
1、特点:与普通的树不同,线段树存取的是某一个区间,它在各个节点保存一条线段。
2、节点储存方式:(结构体)
方式一:数组储存

struct Tree
{
        int leftnode, rightnode; //区间的最左端和最右端
        long long sum;           //该节点储存区间的值
} tree[N *4];          //比常规储存的数组要大四倍(Be careful!)

方式二:链表储存(自己了解)

struct Tree
{
     int Left, Right;
     Tree  *Leftchild , *Rightchild;
 }

3、建树
思路: if(叶节点) 存值,返回;
else 递归,查找左右孩子;
最后一定要记得回溯,维护区间;

void  Build_Tree ( int left , int right , int i )
{
    tree[i].leftnode = left;
tree[i].rightnode = right;     //存储区间最左端、最右端
if( left == right )
tree[i].sum = a[x] ;        //叶子节点,存值
    else
    {
        int  mid = (tree[i].leftnode+ tree[i].rightnode)/2;
 //↑↑↑可以尝试用位运算代替,速度会加快
        Build_Tree (   left  ,  mid  , i/2  );  //遍历左子树
        Build_Tree ( mid + 1 , right , i/2+1); //遍历右子树
        tree[i].sum = ……;(回溯,对线段树进行维护)
    }
}

4、查找线段树上的位置
思路: 递归查询
If(被左儿子覆盖)
查询左儿子;
Else if(被右儿子覆盖)
查询右儿子;
Else //即左右儿子都有
都查询;

int Query_Tree ( int left , int  right , int  i ) //Query:查找、查询
{    //left,right:查找目标的区间
if ( left<= tree[i].leftnode && right >= tree[i].rightnode )  
return tree[i].sum;  //当前结点的区间完全被目标区间覆盖
    else
    {
        int mid = (tree[i].leftnode+ tree[i].rightnode)/2;
        if( left > mid )       //目标区间被左儿子覆盖
            return Query_Tree ( left , right , i/2+1);
        else if (rifgt<= mid ) //目标区间被右儿子覆盖
            return Query_Tree ( left , right , i/2 );
        else                    //目标区间在左右都有分部
    return Query_Tree ( left , right , i/2) + Query_Tree ( left , right , i/2+1 );
    }
}

5、树节点值的更新与维护
思路 更新很容易,查询到位置后替换即可,重要的是维护,回溯即可
If(目标子节点)
更新;
Else
{
If(被左儿子覆盖)
查询左儿子;
If(被右儿子覆盖)
查询右儿子;
}
回溯,更新父节点值;

void  Update_Tree ( int aimnode  , int i ) //update更新 aimnode目标子节点
{
if(tree[i].leftnode == aimnode && tree[i].rightnode == aimnode)
 //找到需要修改的叶子节点
    {
          tree[i].sum = ㊣ ; //更新当前结点 ,㊣为要更新的值
    }
    else       //当前结点是非叶子结点
    {
        int mid = (tree[i].leftnode+ tree[i].rightnode )/2 ; 
        if ( aimnode <= mid ) //目标节点在左儿子中
        {
             Update_Tree ( aimnode , i/2 );
        }
        else if( aimnode > mid ) //目标节点在右儿子中
        {
            Update_Tree ( aimnode , i/2+1 );
        }
        tree[i].sum = tree[i/2].sum + tree[i/2+1].sum; //回溯
    }
}

【练习】
http://codevs.cn/problem/1228/ 苹果树
http://codevs.cn/problem/1080/ 线段树练习
http://codevs.cn/problem/1217/ 借教室
https://www.luogu.org/problem/show?pid=3372 【模板】线段树 1

以上是关于线段树1的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P3372线段树模板1——线段树

线段树

CCF(除法):线段树区间修改(50分)+线段树点修改(100分)+线段树(100分)

2018年6月1号(线段树)

「ZJOI2019」线段树

高级数据结构线段树