线段树部分总结 (单点,区间)更新,区间求和,求最大值(敌兵布阵&I Hate It&A Simple Problem with Integers)

线段树

    如图:

这里写图片描述

线段树是一种二叉搜索树,它将一个大的区间逐层划分为一个个单元区间,而最终的叶子节点代表区间中的各个点,可视为区间。划分方式是二分。为保证节点 i 的子节点是 i*2 和 i*2+1,最底层可能会浪费一些空间,因此空间为 n 的区间需要 n*4 的线段树树来储存。
综上所述,线段树可以是一棵完全二叉树,(浪费空间换来的)可以用数组来储存。
定义树的单个节点的结构体:

struct node{
    int sum;      // 区间和 的记录
    int lazy;     //区间更新的延迟标记,
    int maxx;     //区间的最大值
};
node tree[100010*4];

线段树的几种操作:

1.建树:

众所周知,树的建立一般采用递归的方法,线段树也不例外,所以我们会先储存叶子节点的值,而叶子节点是区间中的一个个点,它的区间和就是自身的值,因此,我们不必另外开一个数组来储存该区间,可以在递归至叶子节点时,直接读入到 sum 或 maxx 中。
一个非叶节点的 sum 是它子节点的 sum 和。
一个非叶节点的maxx是它左右子节点maxx的较大值。
上代码:

//求和建树

void set_tree(int l,int r,int poi){
//poi是节点编号,l,r是该节点代表的区间左右边界
//刚开始时 l=1,r=n,poi=1
    if(l==r){  //如果左右边界相同,即为叶子节点,读入并返回
        scanf("%d",&tree[poi].sum);
        return;
    }
    int m=(l+r)/2;    
    set_tree(l,m,poi*2);      //递归建立左子树
    set_tree(m+1,r,poi*2+1);  //递归建立右子树
    tree[poi].sum=tree[poi*2].sum+tree[poi*2+1].sum;               //初始化 sum 
}
//求最大值建树

void set_tree(int l,int r,int poi){
    if(l==r){
        scanf("%d",&tree[poi].maxx);
        return;
    }
    int m=(l+r)/2;
    set_tree(l,m,poi*2);
    set_tree(m+1,r,poi*2+1);
    tree[poi].maxx=max(tree[poi*2].maxx,tree[poi*2+1].sum);  //与求和建树的区别
}

2.区间求和:

这需要我们将给定区间的所有点的值相加,直接遍历肯定不行,会超时,我们看线段树,每一个叶子节点的值代表了区间的点的值,而叶子节点的值相加后成为它们父节点的 sum ,以此类推。
而我们可以利用这一特点来求给定区间的和,
挑选节点区间组成给定区间,相加区间和即可,见代码详解。

int query(int q_l,int q_r,int l,int r,int poi){
//poi为节点编号,q_l q_r是给定区间的边界,l r是节点poi代表的区间的边界
    if(q_l<=l&&r<=q_r)
        return tree[poi].sum;
//如果 给定区间 包含了 poi区间 ,则返回节点poi的sum值
    int sum=0,m=(l+r)/2;
    if(q_l<=m)         //给定区间在poi左子树占有一部分
        sum+=query(q_l,q_r,l,m,poi*2);//DFS,累积给定区间的和
    else               //给定区间在poi右子树占有一部分
        sum+=query(q_l,q_r,m+1,r,poi*2+1);
    return sum;
}

3.区间求最大值:

同样的,寻找节点区间来表示给定区间,再用所找到的节点区间的maxx值求出给定区间的最大值。
见代码:

int query(int q_l,int q_r,int l,int r,int poi){
    if(q_l<=l&&r<=q_r)
        return tree[poi].maxx;
    int ans_max=0,m=(l+r)/2;
    if(q_l<=m)
        ans_max=max(ans_max,query(q_l,q_r,l,m,poi*2));
    if(m<q_r) 
        ans_max=max(ans_max,query(q_l,q_r,m+1,r,poi*2+1));
    return ans_max;
    //解析同区间求和
}

4.单点更新(只考虑对求和的影响):

将 给定点的值 增加 给定的另一个值,如将点x的值增加y。
从根节点开始采用二分的方法找到该点在树中对应的叶子节点,更新该叶子节点的sum值后,递归回去,更新受影响的节点的sum值。
见代码:

void poi_add(int x,int y,int l,int r,int poi){
//poi是当前节点编号,l r是poi区间边界,x是要求更新的点,y是要求增加的值。
    if(l==r){     
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值