HDOJ1698 Just a hook【线段树---成段更新---lazy标志】-----武科大ACM暑期集训队选拔赛4题
--------2012.05.06--------
居然能撞上原题,算是幸运了一把~
--------2012.05.02---------
第一道成段更新的题目,第一次听说lazy标识,又不知道去哪里找资源学习,所以lazy思想不是很懂。有时间再研究研究~
--------2012.05.03---------
今天中好好理解了一下lazy标志,又观摩了一下代码,在纸上模拟了一下程序运行步骤,得出了以下的结论【仅针对此题】:
(1)lazy标志在成段区间内的确很好用,如果没有lazy标志的话,那么程序还得更新到每个元线段,这样线段树的优势就不复存在。但是如果设置了lazy标识,那么只需要更新线段树中属于该区间的子集中的最长的那些结点线段。比如n为10的线段树中更新5,9区间,只需更新5,5;6,8;9,9三段即可,而不需要更新5,5;6,6;7,7;8,8五段。
(2)在网上搜索关于“线段树 成段更新 延迟标记”的时候,发现lazy标志需要根据具体的题意来设定,有的lazy标志是直接传递,如下边这题;而有的lazy标志是需要累加的;所以,lazy标志的具体用法各种各样,关键是延迟标志的这种思想。
(3)今天把HDU1698的代码认真看了一遍,发现以前经常使用的PushUp(rt)函数如果改成PullUp(rt)更加形象,比如sum[rt]=sum[rt<<1]+sum[rt<<1|1]更像是当前结点把左右孩子的sum值拉上来,而不是左右孩子把自己的sum值推上去;
(4)我发现update貌似是线段树的重中之重,今天阅读代码也让我学习了一种新的update方法,前几天听老师讲线段树的更新的时候,老师PPT展示了一句 if(l>=L&&r<=R),当时没有看懂,就在刚刚,我看懂了~当线段树成段更新的时候,用不到3个if【if(R<=m);if(L>m);if(L<=m&&R>m)】,只要判断左子树和要更新的区间有无交集以及右子树和要更新的区间有无交集即可。当然,update的结束条件也不用必须是L==l&&R==r,只要当前结点表示的区间是要更新区间的子集就可以了。
(5)再有就是PushDown中的那句代码 sum[rt<<1]=lazy[rt]*(len-(len>>1));sum[rt<<1|1]=lazy[rt]*(len>>1);其中(len-(len>>1))和(len>>1)的作用就是计算左右子树区间的长度的;
设当前区间为len:
如果len为奇数,左子树长度为1+(len/2),右子树长度为len/2
如果len为偶数,左子树长度为len/2,右子树长度为len/2
而写成上述格式之后,就不用判断len的奇偶性了。
Problem : 1698 ( Just a Hook ) Judge Status : Accepted
RunId : 5885203 Language : C Author : qq1203456195
/* 1 10 2 1 5 2 5 9 3 */ #include <stdio.h> #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define maxn 111111 int sum[maxn<<2],lazy[maxn<<2]; void PullUp(int rt)//上拉 { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void PushDown(int rt,int len)//下推 { lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt]; sum[rt<<1]=lazy[rt]*(len-(len>>1)); sum[rt<<1|1]=lazy[rt]*(len>>1); lazy[rt]=0; } void build(int l,int r,int rt) { int m=(l+r)>>1; lazy[rt]=0; if(l==r){ sum[rt]=1; return; } build(lson); build(rson); PullUp(rt); } void update(int z,int L,int R,int l,int r,int rt) { int m=(l+r)>>1; if(l>=L&&r<=R){//当前区间是更新区间的子集,则一定要更新 lazy[rt]=z;//标记 sum[rt]=(r-l+1)*z;//更新当前区间 return; } if(lazy[rt])PushDown(rt,r-l+1);//延迟标记下传一层 if(L<=m) update(z,L,R,lson);//左子树上有一部分 if(R>m) update(z,L,R,rson);//右子树上有一部分 PullUp(rt);//上推 } int main() { int t,n,q,x,y,z,i; scanf("%d",&t); for (i=1;i<=t;i++){ scanf("%d",&n); build(1,n,1); scanf("%d",&q); while (q--){ scanf("%d%d%d",&x,&y,&z); update(z,x,y,1,n,1); } printf("Case %d: The total value of the hook is %d.\n",i,sum[1]); } return 0; }
找我内推: 字节跳动各种岗位
作者:
ZH奶酪(张贺)
邮箱:
cheesezh@qq.com
出处:
http://www.cnblogs.com/CheeseZH/
*
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。