从零开始的线段树浅谈

本文介绍了线段树的基础知识,包括线段树的功能、构造方法、查询和修改操作,以及在区间修改时的lazymark标记的重要性。通过两道模板题展示了线段树在维护区间和、区间乘法等操作的应用,强调理解lazymark是掌握线段树的关键。
摘要由CSDN通过智能技术生成

从零开始的线段树浅谈

写在前面

其实早就学会了线段树 但是学得特别夹生 所以经常写挂
今天终于立下决心要做一做真正有技术含量的裸题 才发现自己真的没学会什么
调题很虐心 但是收获很多
这里带来线段树讲解以及比较难的裸题

题目链接

简单题可以直接找RMQ的裸题熟悉操作 不进行赘述
这是第一题 相对简单
这是第二题 难度稍大

先说说线段树

线段树是一种数据结构(废话
功能很强大 我们可以用来维护很多的事情
比如说我们可以维护区间和 最大值 最小值以及支持修改的操作
给出一张线段树的图片 这样好说话一些
这里写图片描述
我们可以看到对于一个数列 我们可以采用如下的方式构造线段树
接下来如果我们要进行操作 针对的都是树上的节点 这样大大减小了我们操作的复杂度
暴力做法我们可以用O(n)的复杂度进行修改并进行预处理 然后用O(1)的复杂度进行查询
但是注意到线段树我们构造的时候采用的是不断二分区间的思想 查询也是从根节点一点一点向下找
所以我们把查询和修改操作的时间复杂度控制在O(logn)。
这样的话我们的复杂度均摊 相对更优
但是美中不足的一点就是 我们在构建线段树的时候 对空间的要求很高
通常都是2n的空间(上图有所展示)
但是有的时候毒瘤出题人卡的话会把线段树卡成大概差不多是一条链
这样的话为保险起见 我们用4n的空间
所以有的时候受到空间的限制 我们需要更高端的操作比如离散化 这个到题目里再分析

接下来是线段树的操作

刚才我已经提到了 对于线段树来讲 我们把数列中的操作转化成为了树上节点的操作
那么第一个出现问题的地方自然是怎么构造这样一棵特别方便的线段树
一般我们的线段树都是通过递归来实现
比如说还是刚才的树
这里写图片描述
这样的话我们在构建的时候
会不断把区间缩小
(1,10)——(1,5)——(1,3)——(1,2)——(1,1)
当最后区间长度只剩下1的时候 我们会发现非常简单了
这个时候节点只有原数列对应的数 很好操作
这个时候处理完我们按照刚才的区间划分
回溯的思想 送回去
这里我给出一段代码 用维护区间和作为例子
小贴士
当前节点的左儿子标号是他的2倍 右节点标号是2倍加1
我用leftson(ls)rightson(rs)表示

inline void buildtree(ll q,ll l,ll r)//q是当前的节点编号 l,r是节点对应的左右端点
{
  tree[q].begi=l,tree[q].endd=r;
  if(l==r)
  {
    tree[q].sum=a[l];//当只剩下一个数的时候 我们的区间和就是这个数
    return;
  }
  ll mid=(l+r)>>1;
  buildtree(ls,l,mid);
  buildtree(rs,mid+1,r);//递归思想 二分区间继续建树 
  tree[q].sum=tree[ls].sum+tree[rs].sum;//这里是把我们的叶子节点往根节点推的过程
}

接下来就是修改操作了 这个跟建树的思想差不多 我们修改分为区间修改和单点修改
大体的想法就是我们找到需要修改的位置 然后修改之后上传
对于区间修改接下来的例题涉及到 一会再说 这里我给出单点修改代码
为了有普遍性 这次我给出的是维护最小值

inline void change(int q,int x,int y)//在第q个节点把第x个位置的数改成y
{
    if(tree[q].begi==tree[q].endd)//叶子节点 直接修改
    {
        tree[q].xiao=y;
        return;
    }
    int mid=(tree[q].begi+tree[q].endd)>>1;
    if(mid>=x) change(ls,x,y);//这里是二分的思想找x这个位置在哪个儿子那边
    else change(rs,x,y);
    tree[q].xiao=min(tree[ls].xiao,tree[rs].xiao);//别忘了上传
}

查询操作不用我说太多 大体的思路是一样的
还是通过二分找到合适的区间之后再合起来

inline int que(int q,int l,int r)//意义同上
{
    if(tree[q].begi==l&&tree[q].endd==r) return tree[q].xiao;//区间正好符合 返回
    int mid=(tree[q].begi+tree[q].endd)>>1;
    if(r<=mid) return que(ls,l,r);//查找对应区间
    else
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值