日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

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

發布時間:2023/11/27 生活经验 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 uscao 线段树成段更新操作及Lazy思想(POJ3468解题报告) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
?

線段樹成段更新操作及Lazy思想(POJ3468解題報告)

標簽:?treequerybuildn2cstruct ?5756人閱讀?評論(0)?收藏?舉報 ?分類: ?

就直接那POJ上面的例題來說吧,http://poj.org/problem?id=3468。

此題題意很好懂:

?給你N個數,Q個操作,操作有兩種,‘Q a b ’是詢問a~b這段數的和,‘C a b c’是把a~b這段數都加上c。

需要用到線段樹的,update:成段增減,query:區間求和

介紹Lazy思想:lazy-tag思想,記錄每一個線段樹節點的變化值,當這部分線段的一致性被破壞我們就將這個變化值傳遞給子區間,大大增加了線段樹的效率。

在此通俗的解釋我理解的Lazy意思,比如現在需要對[a,b]區間值進行加c操作,那么就從根節點[1,n]開始調用update函數進行操作,如果剛好執行到一個子節點,它的節點標記為rt,這時tree[rt].l == a && tree[rt].r == b 這時我們可以一步更新此時rt節點的sum[rt]的值,sum[rt] += c * (tree[rt].r - tree[rt].l + 1),注意關鍵的時刻來了,如果此時按照常規的線段樹的update操作,這時候還應該更新rt子節點的sum[]值,而Lazy思想恰恰是暫時不更新rt子節點的sum[]值,到此就return,直到下次需要用到rt子節點的值的時候才去更新,這樣避免許多可能無用的操作,從而節省時間 。

下面通過具體的代碼來說明之。(此處的函數名和宏學習了小HH的代碼風格)

在此先介紹下代碼中的函數說明:

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

宏定義左兒子lson和右兒子rson,貌似用宏的速度要慢。

PushUp(rt):通過當前節點rt把值遞歸向上更新到根節點

PushDown(rt):通過當前節點rt遞歸向下去更新rt子節點的值

rt表示當前子樹的根(root),也就是當前所在的結點

[cpp]?view plaincopy print?
  1. __int64?sum[N<<2],add[N<<2];??
  2. struct?Node??
  3. {??
  4. ????int?l,r;??
  5. ????int?mid()??
  6. ????{??
  7. ????????return?(l+r)>>1;??
  8. ????}??
  9. }?tree[N<<2];??
這里定義數據結構sum用來存儲每個節點的子節點數值的總和,add用來記錄該節點的每個數值應該加多少

tree[].l tree[].r分別表示某個節點的左右區間,這里的區間是閉區間

下面直接來介紹update函數,Lazy操作主要就是用在這里

[cpp]?view plaincopy print?
  1. void?update(int?c,int?l,int?r,int?rt)//表示對區間[l,r]內的每個數均加c,rt是根節點??
  2. {??
  3. ????if(tree[rt].l?==?l?&&?r?==?tree[rt].r)??
  4. ????{??
  5. ????????add[rt]?+=?c;??
  6. ????????sum[rt]?+=?(__int64)c?*?(r-l+1);??
  7. ????????return;??
  8. ????}??
  9. ????if(tree[rt].l?==?tree[rt].r)?return;??
  10. ????PushDown(rt,tree[rt].r?-?tree[rt].l?+?1);??
  11. ????int?m?=?tree[rt].mid();??
  12. ????if(r?<=?m)?update(c,l,r,rt<<1);??
  13. ????else?if(l?>?m)?update(c,l,r,rt<<1|1);??
  14. ????else??
  15. ????{??
  16. ????????update(c,l,m,rt<<1);??
  17. ????????update(c,m+1,r,rt<<1|1);??
  18. ????}??
  19. ????PushUp(rt);??
  20. }??

if(tree[rt].l == l && r == tree[rt].r) 這里就是用到Lazy思想的關鍵時刻 正如上面說提到的,這里首先更新該節點的sum[rt]值,然后更新該節點具體每個數值應該加多少即add[rt]的值,注意此時整個函數就運行完了,直接return,而不是還繼續向子節點繼續更新,這里就是Lazy思想,暫時不更新子節點的值。

那么什么時候需要更新子節點的值呢?答案是在某部分update操作的時候需要用到那部分沒有更新的節點的值的時候,這里可能有點繞口。這時就掉用PushDown()函數更新子節點的數值。

[cpp]?view plaincopy print?
  1. void?PushDown(int?rt,int?m)??
  2. {??
  3. ????if(add[rt])??
  4. ????{??
  5. ????????add[rt<<1]?+=?add[rt];??
  6. ????????add[rt<<1|1]?+=?add[rt];??
  7. ????????sum[rt<<1]?+=?add[rt]?*?(m?-?(m>>1));??
  8. ????????sum[rt<<1|1]?+=?add[rt]?*?(m>>1);??
  9. ????????add[rt]?=?0;//更新后需要還原??
  10. ????}??
  11. }??
PushDown就是從當前根節點rt向下更新每個子節點的值,這段代碼讀者可以自己好好理解,這也是Lazy的關鍵。

接著就是update操作的三個if語句了,這里我曾經一直不理解,多虧nyf隊友的指點,借此感謝之。


下面再解釋query函數,也就是用這個函數來求區間和

[cpp]?view plaincopy print?
  1. __int64?query(int?l,int?r,int?rt)??
  2. {??
  3. ????if(l?==?tree[rt].l?&&?r?==?tree[rt].r)??
  4. ????{??
  5. ????????return?sum[rt];??
  6. ????}??
  7. ????PushDown(rt,tree[rt].r?-?tree[rt].l?+?1);??
  8. ????int?m?=?tree[rt].mid();??
  9. ????__int64?res?=?0;??
  10. ????if(r?<=?m)?res?+=?query(l,r,rt<<1);??
  11. ????else?if(l?>?m)?res?+=?query(l,r,rt<<1|1);??
  12. ????else??
  13. ????{??
  14. ???????res?+=?query(l,m,rt<<1);??
  15. ???????res?+=?query(m+1,r,rt<<1|1);??
  16. ????}??
  17. ????return?res;??
  18. }??

第一個if還是區間的判斷和前面update的一樣,到這里就可以知道答案了,所以就直接return。

接下來的查詢就需要用到rt子節點的值了,由于我們用了Lazy操作,這段的數值還沒有更新,因此我們需要調用PushDown函數去更新之,滿足if(add[rt])就說明還沒有更新。


到這里整個Lazy思想就算介紹結束了,可能我的語言組織不是很好,如果有不理解的地方可以給我留言,我再解釋大家的疑惑。

PS:今天總算是對線段樹入門了。

這里推薦一下,完全版線段樹網址

下面貼出POJ3468完整的代碼http://www.notonlysuccess.com/index.php/segment-tree-complete/,這里面有很飄逸的線段樹代碼,表示其update和query寫的很巧妙,代碼量也比較少,大家可以去學習。

[cpp]?view plaincopy print?
  1. #include?<iostream>??
  2. #include?<cstdio>??
  3. using?namespace?std;??
  4. const?int?N?=?100005;??
  5. #define?lson?l,m,rt<<1??
  6. #define?rson?m+1,r,rt<<1|1??
  7. ??
  8. __int64?sum[N<<2],add[N<<2];??
  9. struct?Node??
  10. {??
  11. ????int?l,r;??
  12. ????int?mid()??
  13. ????{??
  14. ????????return?(l+r)>>1;??
  15. ????}??
  16. }?tree[N<<2];??
  17. ??
  18. void?PushUp(int?rt)??
  19. {??
  20. ????sum[rt]?=?sum[rt<<1]?+?sum[rt<<1|1];??
  21. }??
  22. ??
  23. void?PushDown(int?rt,int?m)??
  24. {??
  25. ????if(add[rt])??
  26. ????{??
  27. ????????add[rt<<1]?+=?add[rt];??
  28. ????????add[rt<<1|1]?+=?add[rt];??
  29. ????????sum[rt<<1]?+=?add[rt]?*?(m?-?(m>>1));??
  30. ????????sum[rt<<1|1]?+=?add[rt]?*?(m>>1);??
  31. ????????add[rt]?=?0;??
  32. ????}??
  33. }??
  34. ??
  35. void?build(int?l,int?r,int?rt)??
  36. {??
  37. ????tree[rt].l?=?l;??
  38. ????tree[rt].r?=?r;??
  39. ????add[rt]?=?0;??
  40. ????if(l?==?r)??
  41. ????{??
  42. ????????scanf("%I64d",&sum[rt]);??
  43. ????????return?;??
  44. ????}??
  45. ????int?m?=?tree[rt].mid();??
  46. ????build(lson);??
  47. ????build(rson);??
  48. ????PushUp(rt);??
  49. }??
  50. ??
  51. void?update(int?c,int?l,int?r,int?rt)??
  52. {??
  53. ????if(tree[rt].l?==?l?&&?r?==?tree[rt].r)??
  54. ????{??
  55. ????????add[rt]?+=?c;??
  56. ????????sum[rt]?+=?(__int64)c?*?(r-l+1);??
  57. ????????return;??
  58. ????}??
  59. ????if(tree[rt].l?==?tree[rt].r)?return;??
  60. ????PushDown(rt,tree[rt].r?-?tree[rt].l?+?1);??
  61. ????int?m?=?tree[rt].mid();??
  62. ????if(r?<=?m)?update(c,l,r,rt<<1);??
  63. ????else?if(l?>?m)?update(c,l,r,rt<<1|1);??
  64. ????else??
  65. ????{??
  66. ????????update(c,l,m,rt<<1);??
  67. ????????update(c,m+1,r,rt<<1|1);??
  68. ????}??
  69. ????PushUp(rt);??
  70. }??
  71. ??
  72. __int64?query(int?l,int?r,int?rt)??
  73. {??
  74. ????if(l?==?tree[rt].l?&&?r?==?tree[rt].r)??
  75. ????{??
  76. ????????return?sum[rt];??
  77. ????}??
  78. ????PushDown(rt,tree[rt].r?-?tree[rt].l?+?1);??
  79. ????int?m?=?tree[rt].mid();??
  80. ????__int64?res?=?0;??
  81. ????if(r?<=?m)?res?+=?query(l,r,rt<<1);??
  82. ????else?if(l?>?m)?res?+=?query(l,r,rt<<1|1);??
  83. ????else??
  84. ????{??
  85. ???????res?+=?query(l,m,rt<<1);??
  86. ???????res?+=?query(m+1,r,rt<<1|1);??
  87. ????}??
  88. ????return?res;??
  89. }??
  90. ??
  91. int?main()??
  92. {??
  93. ????int?n,m;??
  94. ????while(~scanf("%d?%d",&n,&m))??
  95. ????{??
  96. ????????build(1,n,1);??
  97. ????????while(m--)??
  98. ????????{??
  99. ????????????char?ch[2];??
  100. ????????????scanf("%s",ch);??
  101. ????????????int?a,b,c;??
  102. ????????????if(ch[0]?==?'Q')??
  103. ????????????{??
  104. ????????????????scanf("%d?%d",?&a,&b);??
  105. ????????????????printf("%I64d\n",query(a,b,1));??
  106. ????????????}??
  107. ??
  108. ????????????else??
  109. ????????????{??
  110. ????????????????scanf("%d?%d?%d",&a,&b,&c);??
  111. ????????????????update(c,a,b,1);??
  112. ????????????}??
  113. ????????}??
  114. ????}??
  115. ????return?0;??
  116. }??

總結

以上是生活随笔為你收集整理的uscao 线段树成段更新操作及Lazy思想(POJ3468解题报告)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。