简单易懂线段树-第一步建树-脑袋蒙的来瞧瞧
来源:互联网 发布:java字符串拼接加空格 编辑:程序博客网 时间:2024/05/16 10:04
看了网上好多好多好多的代码,能告诉我为啥都用 '>>'这些符号写的么...是感觉很好看么...(确实挺漂亮问题是本来就很蒙的好吧!)还有啊,确实是我实力太差define 定义左右儿子那每次看到函数里就蒙了,我都替换成正常的了
不过幸好在头脑清醒的时候弄明白了一些,虽然现在查询那还是不太懂
好啦好啦不多说了
首先我先把正常有加减乘除符号的代码给贴出来,当初我就是卡在这了,几乎没见到正常的代码= =本来就蒙蒙的
有这里卡住的看到就懂了就不用往下看了,用hdu1166举例子
//hdu 1166 敌兵布阵
#include<cstdio>#include<cstring>int sum[55555*4];void pushup(int rt){ sum[rt] = sum[rt*2]+sum[rt*2+1];}void build(int l, int r, int rt){ if(l == r) { scanf("%d", &sum[rt]); return ; } int m = (l+r)/2; build(l, m, rt*2); build(m+1, r, rt*2+1); pushup(rt);}void update(int p, int add, int l, int r, int rt){ if(l == r) { sum[rt]+=add; return; } int m = (l+r)/2; if(p <= m)update(p, add, l, m, rt*2); else update(p, add, m+1, r, rt*2+1); pushup(rt);}int query(int L, int R, int l, int r, int rt){ if(L <= l && r <= R)return sum[rt]; int m = (l+r)/2; int ret = 0; if(L <= m) ret += query(L, R, l, m, rt*2); if(R > m)ret += query(L, R, m+1, r, rt*2+1); return ret;}int main(){ int tt, n, i, j, k, x, y; scanf("%d", &tt); for(int cas = 1; cas <= tt; ++cas) { printf("Case %d:\n", cas); scanf("%d", &n); build(1, n, 1); char ch[10]; while(scanf("%s", ch),ch[0]-'E') { scanf("%d%d", &x, &y); if(ch[0]=='A')update(x, y, 1, n, 1); else if(ch[0]=='S')update(x, -y, 1, n, 1); else printf("%d\n",query(x, y, 1, n, 1)); } } return 0;}接下来就是我个人看的理解了,有讲错了欢迎指出
1、首先既然是线段树 既然叫做‘树’ 就说明是树状的对吧,这样我们 怎么用这样的树 来记录各种值呢
答案就是 用树叶来记录每个兵营的人数(我们用这道题举例子, 大家快去读一读是中文题欸)
然后每个节点 就能记录下面叶子的和,这样一层一层的记录就好查了
可是大家还是很蒙,怎么记录,怎么查找,你光这么说我也不知道啊,思想谁都会啊!
别急,才开始,且听我用我的方式慢慢道来
2、开始建立树,函数 void build(int l, int r, int rt) 我们都需要准备什么呢? 我这里不用结构体,就用最直白的
准备: rt, 这个是记录当前结点标号的,就是每个节点的下标(index)
l, r, 这个是 当前第 rt 节点 所记录的 左边 和 右边, 就是l到r之间的和
在函数最外面还需要一个sum[ ]数组,用来记录每个节点记录的值
接下来是一个关键点:当l==r的时候代表什么呢?
对!当l==r 说明我们当前第rt个点只有记录了它自己,因为它下面没有了分支所以他是叶子!!
所以build函数一开始就是这样
<pre name="code" class="cpp"><pre name="code" class="cpp">void build(int l, int r, int rt){ if(l == r)//当前发现是叶子,也就是每个兵营 { scanf("%d", &sum[rt]);//那么我就输入一个要记录的兵营人数 return;//当前找到最底部的叶子了,可以返回了 } 函数还没写完}为什么叶子还用sum记录人数呢,你想想l==r是不是说明也是个区间和?只不过这个区间只有一个值,所以没必要开其他数组混淆视听啦
那么每次我怎么找到l == r啊?你去看主函数 ,是不是写作build(1, n, 1); n是我们兵营数量
也就是说我开始时候l从1开始,r从n开始,1到n就是 rt == 1 记录的区间
然后函数发现它不是叶子,就继续执行
void build(int l, int r, int rt){ if(l == r) { scanf("%d", &sum[rt]); return; } int m = (l+r)/2; //把区间对半分 build(l, m, rt*2);//从l -> m 左半递归 build(m+1, r, rt*2+1);//从 m+1 -> r 右半递归 pushup(rt);}
就这样,不是叶子就对半分,然后左递归,右递归,记住每次标记rt需要*2和*2+1,这是二叉树的规律嘛,我们利用了这个规律
pushup用来每次递归已经确立的左子树和右子树的和
void pushup(int rt){ sum[rt] = sum[rt*2]+sum[rt*2+1];}
到这里建树就基本完成了,我实力太差只能写成这样了,我的想法就是理解如何写出的代码,然后用理解的方式背下来,
至少能记住很久,至于真正理解这样的思想就靠题和时间 还有 之后的返工了,我这里只是讲了怎么理解写出来的
看看有木有人看,有木有人理解,要是需要我再写添加add和查询query,其实这俩和建树异曲同工啦我也想成为厉害的人,给自己个交代我也是能做到很厉害的人,否则一直会笼罩在没有自信之中
大家一起努力啦啦啦
最后提示一下如果用cin和cout一定会超时的,我试着用
int main(){ std::ios::sync_with_stdio(false); std::cin.tie(0); 剩下的主函数接着写}
我试着用这两句话加速cin和cout 才勉强980多ms踩过,而且解除和scanf的绑定之后一开始输出Case什么的会报错
还需要拆开输出,很费劲,就算我不用endl也才省了20多ms(之前看贴吧有人说endl费时),所以老老实实用scanf和printf吧!
- 简单易懂线段树-第一步建树-脑袋蒙的来瞧瞧
- 线段树的建树
- 线段树的两种建树模型
- hdu(2795)——Billboard(简单的线段树的询问,以及建树的技巧)
- Haskell线段树建树
- 关于提高qn画面质量的一个简单方法,美工和程序来瞧瞧
- uva548 (建树)二种方式实现 简单易懂
- 【线段树】入门--引用建树
- 线段树建树思路代码
- 线段树(建树,查询)
- 线段树--按点建树
- 线段树--按区间建树
- [模板]线段树的建树、查询、单点更新、区间更新
- 迟来的第一步
- 简单易懂的使用
- 简单易懂的UML
- hdu 2795 线段树 建树模版
- hdu 5367 线段树动态建树
- grunt-concat
- 对象的引用WeakReference,SoftReference, HardReference,PhantomReference的区别
- Android性能优化Google课程翻译一:Render----OverDraw实战
- MYSQL数据库编程
- tools:context=".MainActivity的作用
- 简单易懂线段树-第一步建树-脑袋蒙的来瞧瞧
- 实现android基于腾讯的定位并判断网络连接
- 响应式设计、改造与优化——互动出版网
- android 中visibility属性VISIBLE、INVISIBLE、GONE的区别
- Unsupported major.minor version 51.0解决办法
- A. Elections(Codeforces Round #316 (Div. 2) 简单题)
- HDU 3065 病毒侵袭持续中(AC自动机)
- Source Insight技巧收集
- sed命令使用总结