2016.5.10 神奇的博主回来了

今天,当然讲的就是加强版的堆,其实算线段树的说。。。

(还有你们看过的留留言好不好呀,谢谢了)

---------------------------

今天说的就是[[[加强版!!!]]]

内容大致就是成段更新,区间求和

也就是说,会用到高大上的lazy tag,那么lazy tag有多lazy呢?神奇的是,如果每次区间全都加一个数,然后每次全都从下面到顶走一遍的话,妥妥的就炸。所以说,利用lazy tag标记的话,其实就是如果有增加的话,只是做一个标签,在有必要的时候再调整,可以大幅缩小运行需要。

因为需要,博主选择一段一段的来写,当然会在结尾贴上完整代码。(所谓的最后一排/最顶上的,看不懂的话,请在上面的博文中找图)

-----------------------------

因为是一段一段的,就不加注释符了。

首先,是建树

struct Node

{

int l,r;

l所表示的是左子的管辖范围就是说能管的区间的最左端

r所表示的是右子的管辖范围就是说能管的区间的最右端

int sum2,lazy;

sum也就是左子右子所管辖的sum的和

lazy也就是lazy tag

为了便于下面的阅读,我将在这里简单介绍一下<<&>>

首先,<<代表的是二进制移位(往左移位)   >>代表的是二进制    往右移位,后面跟的数字就是移位的数。那移位又能干什么呢?比如:

2的二进制是00010是吧

移位一位     00100就是4

移位两位     01000就是8

移位三位     10000就是16

可以看出往左移位1位是乘2,2位是乘4,3位呢,就是乘8等等,说是用起来比乘法要快一些。

反之就是一次除二

} tree[N<<2]; 不是乘2,是乘4!理论上乘3就够了,但是多一点总没坏处,只要不炸就行。

下面就是建树了。

我是潇洒哥,潇洒到根本不处理原数,直接用sum。其实最下面的sum就是原数所以其实更便捷了。

void build(int l,int r,int rt)

这个建树时使用的是递归建树。

{

tree[rt].l = l;

tree[rt].r = r;

完整程序中是build(1,n,1); 当然,不可能全都管辖范围最左端全是1,右端也不可能全是n,这就是为什么递归,当l和r都是初始的1和n时,那么我们在处理的一定是顶端第一个,那么顶端第一个的左端右端就是1和n。

tree[rt].lazy=0;

lazy置空,因为还没有涉及到成段更新的问题,初始化一下

if(l == r)

{

如果说左端和右端一样了,那么就说明是最下面一排的了,那么就要开始输入处理了。

scanf("%d",&sum[rt]);

输入sum,因为最后一排的管辖总和就是自己本身的数

return ;

}

build(lson);

递归左子,你一定会想,三个参数为啥就一个了。。。这里容我解释一下,前面我用了宏

(#define lson l,m,rt<<1

#define rson m+1,r,rt<<1|1)

当然,前面<<和>>也做了相应解释,在这里与或非中的或(|)为什么代表+1,我也不是很懂,所以如果有大神指点一下,谢谢。

乘2是左子,乘2+1是右子。涉及到位运算的相关知识,如果说是1|1,那么肯定答案还是1。但是它前面带着一个左移,那么最后一位一定是0,那么或1一定是+1。附上相关知识链接:http://baike.baidu.com/link?url=8gk3GFWZb0DQexRjbrdMjcRkept4HNaaR1EJ4HvMbgLm5COvKWmXPDoNtZFFayzKcS1mR1YnoKX_HHNd9iVTDq

build(rson);

PushUp(rt);

这个PushUp的作用,接下来揭晓。

}

-----------------------------

下面我会选择先把易懂的查询写完。

int query(int l,int r,int rt)

还是递归,我似乎太喜欢递归了吧。。。

{

if(l == tree[rt].l && r == tree[rt].r)

如果说正好的话

{

return tree[rt].sum2;

那答案就是当前的和

}

PushDown(rt,tree[rt].r - tree[rt].l + 1);

这个PushDown的作用,接下来揭晓。

int res = 0;

这个res就是最后的答案。

int m=(tree[rt].l+tree[rt].r)/2;

m所代表的就是一个分界线

就是说,比如说从3到5查询

一共5个数

最开始的一定是1,所以左子是1,右子是5

m=(1+5)/2=3

就是说跟快排(快速排序http://baike.baidu.com/link?url=zvoAGAi9YDykOgfvG__HCoAHVHDHoG8GP1uB9FyKwGpz3qYnRJjPPs0ZEXK1BYPtlkESo2_57WQDokkERB3tbjDLltgx4_BauWtkelIhGFbvosfCuh3vmuMAti6clzgjBrg5CvGC1AXfFS-T8EIGTb0mAzJUOFRLUvFf2WW9ex90V68KasgStbm0i1wmbrZj

可以了解一下,也很有趣,也是从中间平均分)差不多,从中间分开一下。

if(r <= m) res += query(l,r,rt<<1);

如果说右子还在m的左面,也就说所有的全部都在左边的话,那就处理左子

else if(l > m) res += query(l,r,rt<<1|1);

如果说左子还在m的右面,也就说说有的全部都在右面的话,那就处理右子

else

{

res += query(l,m,rt<<1);

res += query(m+1,r,rt<<1|1);

有左有右的话,就求两边的和

}

return res;

返回结果

}

-----------------------------

因为在build里面先出现的是PushUp,所以也没什么理由,任性决定先写PushUp。

void PushUp(int rt)

数据上推,也就是说(找图看)上面的数据

{

tree[rt].sum2=tree[2*rt].sum2+tree[2*rt+1].sum2;

当前的sum就是左子右子sum的和,和上面博文中的max差不太多

}

-----------------------------

void PushDown(int rt,int m)

下发数据,m是左右子区间长度

{

if(tree[rt].lazy==0) return;

如果说没有要加的lazy那就省事了

tree[rt*2].lazy+=tree[rt].lazy;

有的话,下发给左子

tree[rt*2+1].lazy+=tree[rt].lazy;

下发给右子

tree[rt*2].sum2+=tree[rt].lazy*(m-(m/2));

它左子加的和,一定是它能管到的数量的一半再乘上lazy

因为二叉树左子管到的一定是一半,而每个都是加lazy

但是和左子不同的是,虽然说在数学的角度,(m-(m/2))和(m/2)似乎是一样的,但是说如果是单数这样的话,就会有误差,当然误差会像滚雪球一样越滚越大,所以说要特殊处理一下。

tree[rt*2+1].sum2+=tree[rt].lazy*(m/2);

右子不同。

tree[rt].lazy=0;

必须清空!!!看似微小的错误往往是致命的!如果钥匙没有置空的话,就会一直循环下去!!!一定要注意啊!

}

-----------------------------

还剩一个update

void update(int c,int l,int r,int rt)

{

又是递归。。。big boss来啦,就是所谓的成段更新

首先解决三个参数,第一个就是更新所加的数值,第二个第三个就是成段更新的范围,第4个rt当然就是当前位置不用多说

if(tree[rt].l == l && r == tree[rt].r)

如果位置正好

{

tree[rt].lazy+=c;

当前位置的lazy直接就是更新所加的数

tree[rt].sum2+=(int)c * (r-l+1);

当然这个跟上面PushDown中的一模一样,那为什么还要加呢?

当然这个问题愚蠢的楼主想了一会,本来就要改代码了,发现其实如果在这种情况下根本不会调用PushDown,所以并不存在重复的情况,可以安心使用。

return;

递归须谨慎啊!debug伤不起。。。

}

PushDown(rt,tree[rt].r - tree[rt].l + 1);

记得处理完把数据下发一下。

int m=(tree[rt].l+tree[rt].r)/2;

m还是分界

if(r <= m) update(c,l,r,rt<<1);

其实这一段就跟query没什么区别啦,只不过是更新而已,判断条件都没变呢,还是自己去看吧

else if(l > m) update(c,l,r,rt<<1|1);

else

{

update(c,l,m,rt<<1);

update(c,m+1,r,rt<<1|1);

}

PushUp(rt);

上推一下数据。

}

-----------------------------

主函数非常简单,在这里也不再赘述,当然,前面所答应的完整代码是一定会放的,首先,我找到了一道poj3468,先分析一下题目:

A Simple Problem with Integers

Time Limit: 5000MS Memory Limit: 131072K

Total Submissions: 89915 Accepted: 27990

Case Time Limit: 2000MS

Description

You have N integers, A1,A2, ... ,AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input

The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.

The second line contains N numbers, the initial values of A1,A2, ... ,AN. -1000000000 ≤Ai ≤ 1000000000.

Each of the next Q lines represents an operation.

"C a b c" means adding c to each of Aa,Aa+1, ... ,Ab. -10000 ≤c ≤ 10000.

"Q a b" means querying the sum of Aa,Aa+1, ... ,Ab.

Output

You need to answer all Q commands in order. One answer in a line.

Sample Input

10 5

1 2 3 4 5 6 7 8 9 10

Q 4 4

Q 1 10

Q 2 4

C 3 6 3

Q 2 4

Sample Output

4

55

9

15

Hint

The sums may exceed the range of 32-bit integers.

Source

POJ Monthly--2007.11.25, Yang Yi

这道题所讲的就是成段更新,区间求和。Q是区间查询,求和。C是成段更新。值得注意的是,“The sums may exceed the range of 32-bit integers.”,用int要炸,但是poj的编译系统只认__int 64,Xcode中的lld似乎是不行的。

代码:

#include <iostream>

#include <cstdio>

using namespace std;

const int N = 100005;

#define lson l,m,rt<<1

#define rson m+1,r,rt<<1|1

struct Node

{

int l,r;

__int64 sum,lazy;

int mid()

{

return (l+r)>>1;

}

} tree[N<<2];

void PushUp(int rt)

{

tree[rt].sum=tree[2*rt].sum+tree[2*rt+1].sum;

}

void PushDown(int rt,int m)

{

if(tree[rt].lazy)

{

tree[rt*2].lazy+=tree[rt].lazy;

tree[rt*2+1].lazy+=tree[rt].lazy;

tree[rt*2].sum+=tree[rt].lazy*(m - (m>>1));

tree[rt*2+1].sum+=tree[rt].lazy*(m>>1);

tree[rt].lazy=0;

}

}

void build(int l,int r,int rt)

{

tree[rt].l = l;

tree[rt].r = r;

tree[rt].lazy=0;

if(l == r)

{

scanf("%I64d",&tree[rt].sum);

return ;

}

int m=tree[rt].mid();

build(lson);

build(rson);

PushUp(rt);

}

void update(int c,int l,int r,int rt)

{

if(tree[rt].l == l && r == tree[rt].r)

{

tree[rt].lazy+=c;

tree[rt].sum+=(int)c * (r-l+1);

return;

}

if(tree[rt].l == tree[rt].r) return;

PushDown(rt,tree[rt].r - tree[rt].l + 1);

int m = tree[rt].mid();

if(r <= m) update(c,l,r,rt<<1);

else if(l > m) update(c,l,r,rt<<1|1);

else

{

update(c,l,m,rt<<1);

update(c,m+1,r,rt<<1|1);

}

PushUp(rt);

}

__int64 query(int l,int r,int rt)

{

if(l == tree[rt].l && r == tree[rt].r)

{

return tree[rt].sum;

}

PushDown(rt,tree[rt].r - tree[rt].l + 1);

int m=tree[rt].mid();

__int64 res = 0;

if(r <= m) res += query(l,r,rt<<1);

else if(l > m) res += query(l,r,rt<<1|1);

else

{

res += query(l,m,rt<<1);

res += query(m+1,r,rt<<1|1);

}

return res;

}

int main()

{

int n,m;

while(~scanf("%d %d",&n,&m))

//按位取反

{

build(1,n,1);

while(m--)

{

char ch[2];

scanf("%s",ch);

int a,b,c;

if(ch[0] == 'Q')

{

scanf("%d %d", &a,&b);

printf("%I64d\n",query(a,b,1));

}

else

{

scanf("%d %d %d",&a,&b,&c);

update(c,a,b,1);

}

}

}

return 0;

}

-----------------------------

再给你们点福利,两组测试数据奉上:

10 4

1 5 8 2 5 9 3 5 8 9

Q 3 6

C 4 6 1

Q 5 8

Q 4 5

24

24

9

==================

29 5

2 5 2 3 6 7 3 5 8 3 7 5 4 1 2 6 8 3 6 3 2 5 7 9 8 6 3 1 5

Q 3 19

C 1 28 5

Q 3 6

C 3 6 8

Q 5 10

79

38

78

—————————————————————————————就这样的,然后不懂的可以留言,还有一道线段树poj3667也很适合,会在稍后奉上。

线段树学习心得 poj3468相关推荐

  1. 可持久化线段树学习笔记

    可持久化线段树,即主席树. 每次修改的时候不修改原来的节点,暴力建新节点,充分运用了函数式编程的思想. 模板题:给定一个数列,\(m\) 次询问求区间 \([l,r]\) 内的第 \(k\) 大. 利 ...

  2. SDUTOJ3771_数组计算机(线段树)

    数组计算机 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Problem Description bLue 有一个神器的机器 ...

  3. uscao 线段树成段更新操作及Lazy思想(POJ3468解题报告)

    线段树成段更新操作及Lazy思想(POJ3468解题报告) 标签: treequerybuildn2cstruct 2011-11-03 20:37 5756人阅读 评论(0) 收藏 举报  分类: ...

  4. 学习笔记:可持久化线段树(主席树):静态 + 动态

    学习笔记:可持久化线段树(主席树):静态 + 动态 前置知识: 线段树.线段树分享可以看:@秦淮岸.@ZYzzz.@妄想の岚がそこに 树状数组.\(BIT\)分享可以看:@T-Sherlock.Chi ...

  5. 【学习笔记】线段树详解(全)

    [学习笔记]线段树详解(全) 和三个同学一起搞了接近两个月的线段树,头都要炸了T_T,趁心态尚未凉之前赶快把东西记下来... [目录] [基础]作者:\((Silent\)_\(EAG)\) [懒标记 ...

  6. 权值线段树+动态开点(学习小结)

    首先先上一道板题:把它看懂就OK了,其实这个跟普通的线段树也没太大区别,就是存的是出现的次数. CODE #include<algorithm> #include<cstdio> ...

  7. 数据结构专题-学习笔记:李超线段树

    数据结构专题 - 学习笔记:李超线段树 1. 前言 2. 详解 3. 应用 4. 总结 5. 参考资料 1. 前言 本篇博文是博主学习李超线段树的学习笔记. 2020/12/21 的时候我在 线段树算 ...

  8. 可持久化线段树(静态)【学习笔记】

    (静态)主席树入门 前置知识:动态开点线段树,权值线段树. 1)权值线段树:相当于将线段树当成一个桶,其中的每一个点所代表的区间相当于一段值域.维护的值为这段值域中的一些信息. 例如该图,节点2代表的 ...

  9. [学习笔记]Segment Tree Beats!九老师线段树

    对于这样一类问题: 区间取min,区间求和. N<=100000 要求O(nlogn)级别的算法 直观体会一下,区间取min,还要维护区间和 增加的长度很不好求.... 然鹅, 从前有一个来自杭 ...

最新文章

  1. 验证码 禁止输入中文
  2. Scikit-Learn 机器学习笔记 -- SVM
  3. BZOJ2938: [Poi2000]病毒(AC自动机)
  4. 中标麒麟linux系统安装打印机_中标软件+天津麒麟=中国国产操作系统新旗舰
  5. OA(part2)--Outlier Evaluation Techniques
  6. centos搭建git服务
  7. python写excel
  8. APP加壳技术是否有效?爱加密APP加密服务有什么不同?
  9. ios游戏使得newpad不断迅速增长
  10. 测试wi-fi信号软件是什么,Wi-Fi Inspector - WIFI 信号检测及连接工具
  11. 人工智能专业志愿该如何填报?
  12. Win7系统休眠模式无法唤醒?这招帮你轻松解决问题
  13. 数据埋点日志awk脚本快速入库
  14. android 方向传感器,10.11 传感器专题(2)——方向传感器
  15. (阿里/百度/腾讯)云服务器建站全过程(Ubuntu Server 16.04.1 LTS 64位)
  16. [JavaScript学习记录] 首次运用于网页,做一个简易利息计算器!!!
  17. 如何给单片机烧录程序?
  18. 基于SpringBoot的学生成绩管理系统
  19. Elasticsearch: Query string与Simple query string
  20. Mysql 中MVCC 设计方式

热门文章

  1. 基于餐饮管理系统的软件体系结构设计读书报告
  2. 高保证音频 HDA(High Definition Audio)
  3. 节日生日吃饭提醒小软件
  4. 2016英国国际安防展:科达定位高阶IP视频监控品牌
  5. Windows设置为停止更新
  6. Robot_Framework:基础_关键字
  7. 增值税税率下调!一文带你看懂K/3 WISE应对调整方案(K3服务号文章基础上调整)
  8. edge浏览器突然变的卡顿-解决办法
  9. postman测试下载的接口
  10. excel补充操作技能3--日期函数、表格、文档安全性、邮件合并