C++算法 线段树
线段树这个算法,看起来非常高端,而且很有用处,所以还是讲一下下吧。
温馨提示:写线段树前请做好写码5分钟,调试一辈子的准备^-^
啊直接步入正题……
首先我们考虑一个题目:有一个序列,要做到单点修改单点查询,该怎么做呢?
同学们先不要着急关掉……我们细细分析,像这种题,明显大家都知道……直接暴力就过了嘛……,所以不做分析……
然后我们考虑第二个题目:有一个序列,要做到单点修改区间求和,该怎么做呢?
像传统的for(int i=1;i<=n;i++)ans+=a[i]当然是很香的,但如果遇到一些非常奇怪的题目(数 据 大 到 你 自 闭),这个就没什么用了……
啊这,简单的暴力不能用,就要用复杂的暴力——线段树,当然是简单亿点点的~因为单点修改可以O(1),不存在浪费时间的事情,所以我们用线段树小小的处理一下就好了。
接下来画一个图帮助斯烤:
在这个图上呢……一共有8个点,就是底下那些最小的线段,而我们把他合成了许多部分,每个部分的值就是f[wz*2]+f[wz*2+1](f是每个的值,wz是每个的编号)。
然后就可以把他一直向下,一直向下,直到发现这个区间是被我们要求的区间所包含的,然后这个区间内的数就全部加上,就这么一直向下找找找……,最后就可以算出一个区间的总和。
下面是演示代码~
void qh(long long wz)//wz是现在所处的小块编号
{
if(tree[wz].l>=a&&tree[wz].r<=b)//a和b是我们要求的区间的左右端点
{
zshu+=tree[wz].shu;//全部被包含,直接加上
return;
}
long long mid=(tree[wz].l+tree[wz].r)/2;//不能被包含?分裂一下试试吧
if(b>mid)//和谐小细节~
{
qh(wz*2+1);//如果有被包含,就去右边的小块看看
}
if(a<=mid)//和谐小细节*2~(至于为啥有和谐小细节,以及为什么要这样写,请同学们自己斯烤(这么简单还是可以的8))
{
qh(wz*2);//如果有被包含,也要去左边的小块看看
}
}
嘿嘿简单吧^-^,啊我忘记讲建树了QAQ。没事马上就讲。
建树嘛,直接递归就好了,每次把这个区间分成两份,一直分到l和r一样(就是说这是一个点的值,要输入了),然后获取两个点的值之后就可以向上递归~嗯嗯对,一直递归就建好了,相信大家都有这个写递归的能力,但要实在不会就看看下面的代码吧(温馨提示:线段树用来建树的数组要开到节点数的4倍大,至于为什么我也忘了……):
struct hehe
{
long long l,r,shu,f;
}tree[400005];//tree结构体~
long long mid;
void js(long long ll,long long rr,long long wz)//js,建树的意思。ll和rr分别是左右边界,wz就是他的编号
{
tree[wz].l=ll;//左边界是ll
tree[wz].r=rr;//右边界是rr
if(ll==rr)//左右边界一致,这是一个点
{
scanf("%lld",&tree[wz].shu);
return;
}
long long mid=(ll+rr)/2;//如果这不是一个点,肯定能分成两部分
js(ll,mid,wz*2);//左边
js(mid+1,rr,wz*2+1);//右边
tree[wz].shu=tree[wz*2].shu+tree[wz*2+1].shu;//加起来
}
看,是不是非常简单,接下来为了学的更深一点点,要把题目加难了(是的还要加难,但我相信你们一定可以学会的)
有一个序列,要做到区间增加区间求和,该怎么做呢?
啊这,这个是线段树里最难的一部分(起码我觉得最难),直接下放显然不太现实……,会浪费掉很多不必要的时间。正所谓科技发展在于懒人~,其实我们也可以在不影响结果的情况下偷个懒是吧QWQ。
就像加一样~我知道这两个序列的和,我就没必要去求所有单个序列是多少,加法和这个差不多,我们可以看看有那个序列是全都要加的,然后直接算出它加完之后的值。这样求这个值的时候肯定是不影响计算的(偷懒成功!)。但这时就会有一些同学吐槽:"啊你这个不严谨啊,如果下一次求和是求这个序列的一部分怎么办呢?"其实呢,我刚才也写了是吧……
其实我们也可以在不影响结果的情况下偷个懒是吧
我刚才说的是不影响,但这个操作明显影响了,在哪里影响了呢?就是只加了一个总序列,没有让他的一部分加上(偷懒失败QWQ)。但好像根本没有必要调整他的一部分啊,因为目前根本用不到,没说让做的事情我们还要去做不是浪费时间吗?但又不能不加,怎么办呢?我们可以设定一个懒标记,表示它之前被加了多少,这样呢,每次要求和的时候只要下放这个懒标记,并且加上该加的数,就能达到不说不做,最大程度优化时间,还保证正确hhhhh(偷懒成功)
至于代码的实现也是非常的简单:
void xf(long long wz)
{
tree[wz*2].shu+=(tree[wz*2].r-tree[wz*2].l+1)*tree[wz].f;//每个长度单位都加上tree[wz].f,总值就增加了(tree[wz*2].r-tree[wz*2].l+1)*tree[wz].f
tree[wz*2].f+=tree[wz].f;//之前每一个长度单位要加tree[wz*2].f,现在发现它还要加上tree[wz].f,就把他们两个加起来就好了嘛,很容易的。
tree[wz*2+1].shu+=(tree[wz*2+1].r-tree[wz*2+1].l+1)*tree[wz].f;
tree[wz*2+1].f+=tree[wz].f;//同理
tree[wz].f=0;//这里已经加过了,要清零的,就像转账一样,你给另一个人转了钱,你钱就没了。
}
上方是下放懒标记的代码,下方是区间修改代码:
void xg(long long wz)
{
if(tree[wz].l>=a&&tree[wz].r<=b)
{
tree[wz].shu+=(tree[wz].r-tree[wz].l+1)*c;//更改值
tree[wz].f+=c;//增加懒标记
return;
}//啊后面的都说过,不打了…
tree[wz].shu+=(min(tree[wz].r,b)-max(tree[wz].l,a)+1)*c;
long long mid=(tree[wz].l+tree[wz].r)/2;
if(b>mid)
{
xg(wz*2+1);
}
if(a<=mid)
{
xg(wz*2);
}
}
至于加了懒标记以后别的代码也是要动的。
比如在原来的求和代码里多了一个xf(wz);
至于加在哪里同学们自己斯烤吧(我太仁慈了)
啊我再放一道线段树模板题,大家可以秒掉来吊打我:模板题传送们
好了我觉得我讲完了,如果有什么不好或漏掉的地方大家可以及时评论,我会更改的。
C++算法 线段树的更多相关文章
- [莫队算法 线段树 斐波那契 暴力] Codeforces 633H Fibonacci-ish II
题目大意:给出一个长度为n的数列a. 对于一个询问lj和rj.将a[lj]到a[rj]从小到大排序后并去重.设得到的新数列为b,长度为k,求F1*b1+F2*b2+F3*b3+...+Fk*bk.当中 ...
- 浅谈算法——线段树之Lazy标记
一.前言 前面我们已经知道线段树能够进行单点修改和区间查询操作(基本线段树).那么如果需要修改的是一个区间该怎么办呢?如果是暴力修改到叶子节点,复杂度即为\(O(nlog n)\),显然是十分不优秀的 ...
- [算法]线段树(IntervalTree)
转载请注明出处:http://www.cnblogs.com/StartoverX/p/4617963.html 线段树是一颗二叉搜索树,线段树将一个区间划分成一些单元区间,每一个区间对应线段树的一个 ...
- POJ 3368 Frequent values RMQ ST算法/线段树
Frequent values Time Limit: 2000MS Memory Lim ...
- [bzoj4372] 烁烁的游戏 [动态点分治+线段树+容斥原理]
题面 传送门 思路 观察一下题目,要求的是修改"距离点$u$的距离一定的点权值",那这个就不能用传统的dfs序类算法+线段树维护,因为涉及到向父亲回溯的问题 看到和树上距离相关的东 ...
- 算法手记 之 数据结构(线段树详解)(POJ 3468)
依然延续第一篇读书笔记,这一篇是基于<ACM/ICPC 算法训练教程>上关于线段树的讲解的总结和修改(这本书在线段树这里Error非常多),但是总体来说这本书关于具体算法的讲解和案例都是不 ...
- RMQ问题(线段树+ST算法)
转载自:http://kmplayer.iteye.com/blog/575725 RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ ...
- nyoj 119士兵杀敌(三)(线段树区间最值查询,RMQ算法)
题目119 题目信息 执行结果 本题排行 讨论区 士兵杀敌(三) 时间限制:2000 ms | 内存限制:65535 KB 难度:5 描写叙述 南将军统率着N个士兵,士兵分别编号为1~N,南将军常 ...
- 线段树:CDOJ1591-An easy problem A (RMQ算法和最简单的线段树模板)
An easy problem A Time Limit: 1000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) Pr ...
随机推荐
- 题解 Luogu P1514 【引水入城】
有一种神奇的算法叫做floodfill 就是一个n*m的矩阵,a[i][j]为当前高度,我们可以任选一个点倒水,开始bfs,如果要搜的点没有被搜到过,并且高度小于当前的点,我们就把这个点加入队列中 而 ...
- [转]Springboot项目的接口防刷的实例
来源:微信公众号 马士兵 原地址:https://mp.weixin.qq.com/s/tHQcWwIt4c41IUnvCQ2QWA 说明:使用了注解的方式进行对接口防刷的功能,非常高大上,本文章仅供 ...
- kubernetes ingress 重定向地址错误
环境: 有两个 kubernetes 集群: 一个版本为1.11,后面使用A集群代替,ingress 镜像版本为 0.19(quay.io/kubernetes-ingress-controller/ ...
- 001_解析go语言中的闭包
go语言中的闭包,是大家学习go语言的一个大难点,笔者在学习时候也是痛苦不堪,在来回对比了其它语言的用法,并且查阅了很多网上的文章,终于对闭包有了一个较为清晰的认识,以下就是关于闭包的解析 首先看一个 ...
- 大型企业都在用的Python反爬虫手段,破了它!
SVG 映射反爬虫 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手.很多已经做案例的人,却不知道如何去学习更加高深的知识.那么针对这三类人 ...
- 20、FlyWeight 享元模式
池化的思想 1.Flyweight享元模式 运用共享技术有效地支持大量细粒度对象的复用.系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用.由于享元模式要求能够共享的对象必 ...
- java 增强for循环与泛型
一 增强for循环 增强for循环是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的.它的内部 原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作 ...
- K8S(11)配置中心实战-单环境交付apollo三组件
k8s配置中心实战-交付apollo三组件 目录 k8s配置中心实战-交付apollo三组件 1 apollo简单说明 1.1 apollo最简架构图: 1.2 apollo组件部署关系 2 为app ...
- 编译hotspot8
编译hotspot8 ubuntu desktop 18 全新准备与编译过程再记录下: # 建议使用此gcc和g++版本,过高版本比如gcc7或引发编译报错 sudo apt-get install ...
- equals&hashCode
经典规则:如果重写了equals,必须重写hashCode 为什么??? 这个规则考虑的应用场景:如果你的对象可能放入HashMap,HashSet,不重写会出问题 问题场景: new一个对象,重写了 ...