线段树相关(研究总结,线段树)

线段树是信息学竞赛中的一种常用数据结构,能够很方便的进行区间查找和修改操作。

引入

假设我们现在有一列数,我们需要支持一下操作:

1.修改某个数的值
2.询问一段区间的和

我们很容易想到朴素的做法,用一个数组存下所有的值,如果是修改操作就直接修改,如果是询问就循环统计一遍。但这样效率不高。

或许有人可以想出另外一个算法,就是用前缀和来优化,Sum[i]表示1到i的和,这样方便了询问操作,但对于修改操作,就要修改很多Sum的值,效率同样不高。

怎么办呢?我们可以用线段树解决

线段树

对于下面这个区间

我们可以把其分成两个

再对分出的两个区间进行分离操作
像这样重复操作,我们就可以得到一棵线段树

一些小性质与本文习惯约定

通过观察我们可以发现,如果按从上到下,从左到右的顺序,把每一个区间看作一个点并给其编号,我们可以得出如下规律:

对于每一个节点i,我们假设其代表的区间是[x,y],定义mid=(x+y)/2,那么,它的左区间[x,mid]的编号就是i*2,而它的右区间[mid+1,y]编号就是i*2+1

为了方便叙述,下面我们称i*2为i的左子节点,i*2+1为i的右子节点
另外,本文中约定线段树的节点编号从1开始

线段树的简单操作

现在有了线段树,但它有什么用呢?
先简单的说一下,假设我们现在要查找[x,y]的和,并且我们现在正好在线段树的这个[x,y]区间,那么我们就可以直接返回这个点上的Sum域(当然要提前确定好,这是可以在建树的时候顺带完成的)
比如说,对于下面这个数组:

我们想建立一棵线段树来处理其任意两点之间的和,那么我们可以这样:
对于每一个线段树的节点,我们维护一个Sum表示当前线段树的节点中覆盖的区间的和,表示出来就是这样:

而由于我们知道线段树的性质是左儿子是i*2而右儿子是i*2+1,所以我们甚至不需要存下对应的左儿子右儿子编号。
首先我们来讲一下查询操作
在本例中,查询是查询一个区间内的值之和,假设当前我们要查询的区间是[l0,r0],当前我们所在的线段树的节点是now(开始时就是1啦),当前我们所在的线段树节点的左右区间是[l,r],再定义mid=(l+r)/2,也就是中间结点。
我们定义查询的函数是Query(l0,r0,l,r,now)我们会碰到如下的三种情况:
1.查询区间完全在左区间内(我们用透明的框表示当前所在的线段树区间,用蓝色的框表示我们的查询区间,红色的框代表mid所在)

对于这种情况,我们直接递归地调用查找左子树区间就可以了,即Query(l0,r0,l,mid,now*2)
2.类似的,查询区间完全在右区间内时

递归调用Query(l0,r0,mid+1,now*2+1)
3.稍微复杂一点的就是这第三种情况,查询区间横跨左右

这个时候我们要分别对左区间和右区间进行递归查询并累加Query(l0,mid,l,mid,now*2)+Query(mid+1,r0,mid+1,r,now*+1)
其实还有第四种情况,就是l0==l,r0==r,此时直接返回该区间的Sum值就可以了
总结一下,代码如下:

int Query(int l0,int r0,int l,int r,int now)
{if ((l0==l)&&(r0==r))return T[now].data;//T就是线段树,data就是值域int mid=(l+r)/2;if (l0>=mid+1){return Query(l0,r0,mid+1,r,now*2+1);}elseif (r0<=mid){return Query(l0,r0,l,mid,now*2);}else{return Query(l0,mid,l,mid,now*2)+Query(mid+1,r0,mid+1,r,now*2+1);}
}

然后我们来讲一下修改操作
相对于查询操作,修改操作相对好理解。
同样也是进行递归的操作,若修改的数在左区间,则递归到左子树,否则递归到右子树。
但要注意的是,修改的时候要记得一路修改所有经过的线段树节点的值。

void Updata(int num,int data,int l,int r,int now)//num是我们要修改的数的编号,data是我们要把num修改成什么,l和r分别是当前所在线段树的now节点的左右区间端点
{if (l==r){T[now].data=data;return;}int mid=(l+r)/2;if (num<=mid)Updata(num,data,l,mid,now*2);//分别进入左右子树elseUpdata(num,data,mid+1,r,now*2+1);T[now].data+=data;//注意一路修改
}

在有些题目中,递归调用可能会爆栈,而又因为修改操作的特殊性(它不会涉及到同时操作两个区间),我们可以把递归的方式改成不递归的,,其原理与递归方式一样

void Updata(int num,int data)
{int now=1;int l=1,r=n;do{int mid=(l+r)/2;T[now].data+=data;if (l==r)break;if (num<=mid){r=mid;now=now*2;}else{l=mid+1;now=now*2+1;}}while (1);return;
}

线段树的其他操作

在了解了线段树的基本操作后,相信读者已经对线段树有了基本的了解。
线段树其实还有很多操作,它们都是建立在对线段树的理解上面的,笔者这里仅列出常用的一种,其它的读者可以在遇到相关题目时自行推导。
在上文中,我们讲到了线段树的修改操作,但准确地说,这是线段树的单源修改操作,如果我们要对数列的一段区间的数进行修改呢?(比如说,我们要使得[x,y]中的每一个数都加上一个输入的数)
一种解决方法就是调用(y-x+1)次单源修改操作,但这样时间复杂度太高。
我们回想一下线段树的原理:它是区间的操作。那我们能否把区间修改与区间操作相结合起来呢?
当然可以。我们在每一个线段树的值域中再引入一个Lazy,或者叫做迟缓标记,在我们上面的例子中(即使得[x,y]中的每一个数都加上一个数),它表示的就是在当前线段树节点所覆盖的每一个点上都加上Lazy。
举个例子,现在我们要在一个[1,10]的数列中,给l0=1,r0=5内的所有数都加上1,那么我们在递归修改的时候,首先进入的是l=1,r=10的区间,然后进入l=1,r=5的区间,这是我们发现l0==l且r0==r,所以我们直接给该节点上的Laze+=1。
那么对应的,因为我们加入了迟缓标记,我们就要修改一下查询和修改操作。在每一次进入下一层时,首先要下放当前点中的Lazy标记,因为加入迟缓标记后,该层下面的点都是没有修改的,此时我们要向下查询的话就要临时把Lazy标记中的内容向下传递,这就相当于迟缓了修改操作(现在知道为什么叫迟缓标记了吧)
查询操作:

int Query(int l0,int r0,int l,int r,int now);
{if ((l0==l)&&(r0==r))//如果符合就直接返回{return T[now].data+T[now].Lazy*(l-r+1);}int Lazy=T[now].lazy;//下放Lazy标记T[now].data+=Lazy*(l-r+1);T[now*2].lazy+=Lazy;T[now*2+1].lazy+=Lazy;T[now].lazy=0;int mid=(l+r)/2;if (r0<=mid)//向下递归return Query(l0,r0,l,mid,now*2);elseif (l0>=mid+1)return Query(l0,r0,mid+1,r,now*2+1);elsereturn Query(l0,mid,l,mid,now*2)+Query(mid+1,r0,mid+1,r,now*2+1);
}

修改操作:

void Updata(int l0,int r0,int l,int r,int now,int data)
{if ((l0==l)&&(r0==r)){T[now].lazy=data;return;}int Lazy=T[now].lazy;//向下传递LazyT[now].data+=Lazy*(l-r+1);T[now*2].lazy+=Lazy;T[now*2+1].lazy+=Lazy;T[now].lazy=0;int mid=(l+r)/2;if (r0<=mid)Updata(l0,r0,l,mid,now*2,data);elseif (l0>=mid+1)Updata(l0,r0,mid+1,r,now*2+1,data);else{Updata(l0,mid,l,mid,now*2,data);Updata(mid+1,r0,mid+1,r,now*2+1,data);}return;
}

好题推荐

请到我的博客右侧线段树分类中查看
欢迎大佬查错,谢谢

转载于:https://www.cnblogs.com/SYCstudio/p/7217583.html

线段树相关(研究总结,线段树)相关推荐

  1. 如何在vs中创建r树索引代码_线段树详解与实现

    此篇文章用于记录<玩转数据结构>课程的学习笔记 什么是线段树 线段树也被称为区间树,英文名为Segment Tree或者Interval tree,是一种高级的数据结构.这种数据结构更多出 ...

  2. AcWing 蓝桥杯AB组辅导课 05、树状数组与线段树

    文章目录 前言 一.树状数组 1.1.树状数组知识点 1.2.树状数组代码模板 模板题:AcWing 1264. 动态求连续区间和 例题 例题1.AcWing 1265. 数星星[中等,信息学奥赛一本 ...

  3. 树套树 ----- P1975 [国家集训队]排队(树状数组套权值线段树求动态逆序对)

    解题思路: 首先我们知道交换两个数a[l]和a[r]a[l]和a[r]a[l]和a[r]影响到的区间是[l+1,r−1][l+1,r-1][l+1,r−1] 对于a[l]a[l]a[l],我们要减去[ ...

  4. D-query SPOJ - DQUERY(求区间不同数的个数)(树状数组||线段树+离散)(主席树+在线)

    English Vietnamese Given a sequence of n numbers a1, a2, -, an and a number of d-queries. A d-query ...

  5. H - Hello Ms. Ze(树状数组套主席树,线段树上二分)

    H - Hello Ms. Ze 给定nnn种不同的材料,第iii种材料有aia_iai​个,有mmm个操作,操作分为两类: 把第xxx种材料修改为yyy个, 只用[l,r][l, r][l,r]区间 ...

  6. 【用学校抄作业带你走进可持久化线段树(主席树)】可持久化线段树概念+全套模板+例题入门:[福利]可持久化线段树)

    我似乎很少写这种算法博客 可持久化线段树概念 概念介绍(类比帮助理解) 简单分析一下时间和空间复杂度(内容池) 模板 结构体变量 建树模板 单点修改模板 单点查询模板 区间修改模板(pushup) 区 ...

  7. BZOJ.4553.[HEOI2016TJOI2016]序列(DP 树状数组套线段树/二维线段树(MLE) 动态开点)

    题目链接:BZOJ 洛谷 \(O(n^2)\)DP很好写,对于当前的i从之前满足条件的j中选一个最大值,\(dp[i]=d[j]+1\) for(int j=1; j<i; ++j)if(a[j ...

  8. 2019南昌网络赛  I. Yukino With Subinterval 树状数组套线段树

    I. Yukino With Subinterval 题目链接: Problem Descripe Yukino has an array \(a_1, a_2 \cdots a_n\). As a ...

  9. 数据结构:树套树-替罪羊树套权值线段树

    BZOJ3065 本题是在BZOJ上的处女A,实在不应该拿这样一道题来开头 平衡树套线段树应该是树套树问题里比较难的一种了,当然我记得还有一个替罪羊树套Trie树的题,我是不信自己能写出来的. 外层的 ...

  10. 树状数组及线段树入门(SDNU1665-1668)

    目录 前言 树状数组 先导 单点修改区间查询 区间修改区间查询 线段树 先导 单点修改区间查询--递归形式 单点修改区间查询--非递归形式 区间修改区间查询--递归形式 区间修改区间查询--非递归形式 ...

最新文章

  1. C#部分---函数添加基本格式;
  2. android 11微信,QQ,支付宝无法调用的问题
  3. 概要设计说明书案例_逆向前行,趁势而为外贸学院线上教学优秀案例展(七)...
  4. 极客大佬用什么电脑_极客特惠:笔记本电脑,高清电视和免费应用
  5. MFC,QT与WinForm,WPF简介
  6. 当 dotnet-monitor 遇上 Prometheus, 是种什么样的体验?
  7. 当设计模式遇上 Hooks
  8. 图解分布式架构的发展和演进 | 技术干货
  9. 成为中国最好的Magento开发公司
  10. 钱多多被立案侦查,曾多次因借款合同纠纷被起诉
  11. 图片在mysql中的储存_如何在MySQL中直接储存图片
  12. python将输出结果写入csv_Python怎么把输出整体写入CSV文件
  13. win10计算机桌面路径,win10桌面路径是什么?如何修改win10桌面文件路径?
  14. 无纸化会议系统连接服务器失败,无纸化会议系统使用注意事项及注册/更新流程...
  15. 详细Ubuntu系统修改默认软件下载源
  16. 中学生怎样才能合理使用计算机,浅析中学生计算机的使用
  17. 正式工作后的一些变化和感受
  18. C++在指定目录生成txt文件
  19. 编写一个程序,提示用户输入一个四位整数(例如:1234),然后显示输入数的相反排序(例如:4321)并计算该数字中每个数字的总和(总和=4+3+2+1)。
  20. stc32G库函数(二)——定时器

热门文章

  1. Intellij IDEA|phpstorm 相关收藏
  2. php日志,记录日志
  3. LayaAir cacheAs 缓存与 visible 隐藏
  4. 解决 VMware 新装 CentOS 7.* 连不上网络
  5. mybatisplus 增删改查(普通)
  6. Git命令之查看及设置用户名邮箱
  7. 阶段3 1.Mybatis_09.Mybatis的多表操作_9 mybatis多对多操作-查询用户获取用户所包含的角色信息...
  8. 小论坛 之Linux服务器搭建Apache PHP mysql 环境
  9. 剑指offer-数值的整数次方
  10. mui多层tab切换上拉加载的实现