[洛穀日報第22期]可以代替線段樹的樹狀數組?

2021-01-07 洛谷科技

樹狀數組進階(1)

*前置知識:樹狀數組,如果沒有學過請右轉這篇文章:傳送門,前綴和,差分。

*本文代碼均未經編譯,如有錯誤請指出。

*前排cqy,826755370,喜歡EXO,bigbang,BTS,ikon,nine percent,本命世勳。

引入

大家學了線段樹與樹狀數組後,一定會覺得樹狀數組比線段樹好寫(背)多了,常數也小多了(分析lowbit操作,每次操作中每個節點被訪問的概率是1/2,所以常數是1/2)但是美中不足的是樹狀數組不能區間修改+區間查詢啊。事實上,樹狀數組可以做到這些,還可以查詢第k大(小)值。

1、最簡單的單點修改+區間查詢

這個就不要我講了吧,直接上代碼:

2、 稍微難一點點的區間修改+單點0查詢

這裡用的是差分的思想。

我們設原數組為a,則我們需要維護一個差分數組delta: delta[i]=a[i]-a[i-1]delta[i]=a[i]a[i1]

那麼我們可以得到: $a[i]=\sum_{j=1}^idelta[j]

現在a[i]被表達成了一個數組內連續的幾個元素的,這樣我們就解決了查詢的問題,那麼我們該如何修改呢?

當我們需要將區間[l,r]上的每個數都加上x時,因為d數組是個差分數組,所以我們可以直接在樹狀數組上將delta[l]加上x,delta[r+1]減去x即可,代碼如下:

重點來了!

3、區間修改+區間查詢

我們還是需要引入delta數組,這裡的delta[i]表示區間a[i...j]都需要加上的值的和。那麼當我們需要將區間[l,r]上的每個數都加上x時,我們還是可以直接在樹狀數組上將delta[l]加上x,delta[r+1]減去x。

那麼問題來了,如何查詢區間[l,r]的和?

我們設a[1...i]的和為sum[i],根據delta數組的定義,則:

這樣我們就不難看sum[i]是由哪三個部分組成的了。我們需要用一個asum數組維護a數組的前綴和,delta1與delta2兩個樹狀數組,delta1維護delta數組的和,delta2維護delta[i]*i的和,代碼如下:

等等……線段樹不是還能查最大最小值嗎,事實上樹狀數組也能查。

4、區間最值

哦,我知道了,我來建樹:

如果你是這麼寫的,那麼恭喜你……寫錯了。

以下內容有些難理解,先放一張圖:

我們回想一下,當初剛學樹狀數組時為什麼很多人總會說樹狀數組不能用來求最值。那是因為樹狀數組可以看作是一種前綴和,求和時可以用ans=ans[r]-ans[l-1]的性質,但是求最值無法滿足這種減法的性質。分析一下我們剛才的代碼,這麼寫也不是不對,而是每次查詢前都必須初始化,時間複雜度難以接受,讓我們換一種寫法試一試:

現在更新完某個數,之前的元素的值都是正確的了,顯而易見,建樹的時間複雜度是O(nlogn)的。

那麼我們該如何修改呢?當然不能在父親節點上直接修改啦(手動滑稽),換了一種建樹的方式就是為了維護c數組的正確性,修改同樣也要保證c數組的正確性,那麼在更新父親節點時,我們就需要查詢它所有的兒子節點,代碼如下:

不難發現,每層循環都是lowbit操作,時間複雜度為O(王逸松)O(log(n)*log(n)),其實也沒多慢,當n=1e5時,logn約等於16,就把一個logn當成常數看,線段樹常數也挺大的啊,樹狀數組代碼量還這麼少。

修改是修改完了,那麼問題來了,我們該如何查詢?

假設當前查詢的區間是[l,r],那麼我們從r到l對每一個c數組的元素所控制的葉子節點進行判斷。假設現在進行到了第i項,那麼顯然易得(看圖):該數控制的a數組的元素是 [i-lowbit(i)+1,i]。設L=i-lowbit(i)+1,R=i。如果l<=L<=r那麼就將c[L]加入最值的判斷中,接著L--……,否則的話就只對第R個元素加入,然後R--……,代碼如下:

顯然,時間複雜度是 O(logn*logn)O(lognlogn) 的。

完整代碼如下:

5、二維情況下的樹狀數組

*單點修改+區間查詢 這個我並不想講,唯一需要注意的點寫在注釋裡了,直接上代碼:

*區間修改+單點查詢 在開始之前,我們先回想一下二維的前綴和如何求和:

這個應該沒什麼問題吧?

那麼我們和一維情形一樣,開一個差分數組delta。

思路到此結束,希望大家可以自己寫代碼。

區間修改+區間查詢 這裡只給出一句話思路,希望大家自己思考,寫出代碼。

思路:將以上式子變形成類似一維情形下最終得到的式子。

區間最值 有這麼毒瘤的題目嗎……

完結撒花!★,°:.☆( ̄▽ ̄)/$:.°★ 。

本文發布於洛穀日報,特約作者:Chanis

原文地址:https://www.luogu.org/blog/Chanis/super-BIT

相關焦點

  • [洛穀日報第46期]線段樹的擴展之淺談zkw線段樹
    0 閱讀本文前請先閱讀【洛穀日報#4】淺談線段樹(https://pks-loving.blog.luogu.org/senior-data-structure-qian-tan-xian-duan-shu-segment-tree)本文主要是上面文章的延伸,所以上文有講的東西本文就不詳細講了QwQ筆者的測試代碼可能寫醜了
  • [洛穀日報第4期]淺談線段樹——Segment Tree
    一、簡介線段樹ps: _此處以詢問區間和為例。實際上線段樹可以處理很多符合結合律的操作。對,線段樹就是分塊思想的樹化,或者說是對於信息處理的二進位化——用於達到O(logn)級別的處理速度,log以2為底。(其實以幾為底都只不過是個常數,可忽略)。而分塊的思想,則是可以用一句話總結為:通過將整個序列分為有窮個小塊,對於要查詢的一段區間,總是可以整合成k個所分塊與m個單個元素的信息的並(0≤k,m≤sqrt{n})。
  • [洛穀日報第79期]二進位與位運算
    所以我們可以得出 x & (-x)=2^i主要的應用就是樹狀數組了,這個我不說你們也懂當然也可以用來計算二進位數中出現了多少個 1快速判斷奇偶性這個上面說了,不再多提>你要相信,這種寫法真的不是在嘚瑟,這樣真的會快那麼一點的,說不定這麼一點常數就能讓你卡過一道題bitsetbitset 是 C++ 裡的一個類,我們可以簡單認為它就是一個 01 數組,可以對數組的每一位進行賦值,但它還支持很多神奇的操作,比如前面介紹過的位運算,比如 「&」 運算和 「
  • 矽谷面試經典算法之-線段樹、樹狀數組
    矽谷面試經典算法之-線段樹、樹狀數組線段樹又叫Segmentation Tree,樹狀數組又叫Binary indexed tree
  • 線段樹,看這一篇就夠了
    定義線段樹(segment tree),顧名思義, 是用來存放給定區間(segment, or interval)內對應信息的一種數據結構。與樹狀數組(binary indexed tree)相似,線段樹也用來處理數組相應的區間查詢(range query)和元素更新(update)操作。
  • [洛穀日報第45期]談談關於初賽的那些事
    下面主要提一下數學知識和CCF讚歌相關的,相信大家在其他的洛穀日報裡能夠看到許多圖論與數據結構相關的內容,而硬體知識與計算機發展史也應該已經有所了解。id=657&hash=64DD5A)7月16日-22日,由中國計算機學會(CCF)主辦,長沙市雅禮中學承辦的第35屆全國青少年信息學奧林匹克競賽(NOI2018)在星城長沙隆重舉行。(點燃夢想,燃燒激情——NOI2018在長沙順利舉行http://www.noi.cn/newsview.html?
  • [洛穀日報第35期]淺談自適應Simpson法
    說到不規則圖形,我們往往都是先從曲邊梯形開始曲邊梯形 ABCD 就是下圖中曲線 AB 、線段 AC 、 CD 、 DB 圍成的圖形,我們想要求出它的面積對於這個問題,我們的解決方案是:把曲邊梯形分成 n 段,每一段用一些規則的幾何圖形去近似,然後把每一段的面積加起來,這樣我們就得到近似值了可以看出
  • 圖解:什麼是線段樹?
    簡介 線段樹是在算法競賽中常用來維護區間信息的數據結構,同時,線段樹的理解難度較樹狀數組低(當然是在理解遞歸的前提下)。最簡單的方法是堆式建樹即像下面這樣,我們可以用一個數組直接存儲線段樹的節點,節點在數組中下標如圖:
  • [洛穀日報第44期]強勢圖解AC自動機
    然後就可以利用它進行多模式匹配了。構建fail指針,可以參考KMP中構造next數組的思想。我們利用部分已經求出fail指針的結點推導出當前結點的fail指針。可以發現,眾多交錯的黑色邊將字典樹變成了字典圖。圖中省略了連向根節點的黑邊(否則會更亂)。
  • 樹狀數組,看這一篇就夠了
    舉例來說,樹狀數組所能解決的典型問題就是存在一個長度為n的數組,我們如何高效進行如下操作:1.update(idx, delta):將num加到位置idx的數字上。2.prefixSum(idx):求從數組第一個位置到第idx(含idx)個位置所有數字的和。
  • GGTalk 中有哪些知識 - 線段樹基礎與 RMQ 問題
    [28:30] 磊子:國內某特別愛招聘的大廠問過這麼一道題:一個數組,要求得任意一個區間段內最大的數是多少。如果大家了解的話,就知道這題其實在考線段樹....當有 n 個元素時,對區間的操作可以在 O(log n) 複雜度時間內完成。另外,從二叉樹結構上,可以看出線段樹是一棵完美二叉樹(Perfect Binary Tree),即所有葉子的深度都相同,並且每個結點要麼是葉子,要麼有 2 個子樹。不同場景下線段樹的功能根據結點維護的數據含義不同,線段樹可以提供不同的功能來滿足各種各樣的區間場景。
  • 什麼是線段樹?
    一、概念解析 這次來介紹一個數據結構 - 線段樹。在平時刷題或是工作中,經常會遇到這麼一個問題,「給定一個數組,求出數組某段區間的一些性質」。比如給定一個數組 [5,2,6,1,-4,0,9,2],讓你求出區間 [1,4] 上所有元素的和,在這個例子中,答案是 2 + 6 + 1 + (-4) = 5。
  • 數據結構:線段樹入門與實踐
    _nums[i:j + 1])那如果使用線段樹呢?這裡我們使用數組存儲的方式來實現。首先線段數數組初始化可以直接套用上面說到的兩種方式,關鍵是 update 與 sumRange。而對於上面提到的兩種方式其實 update 和 sumRange 在解決的的時候實際代碼是一樣的。
  • 初級樹狀數組 leetcode 練習題
    分享幾個簡單的樹狀數組練習題。一、背景之前分享了《樹狀數組模板》和《離散化模板》,今天來看幾道練習題。
  • [洛穀日報第21期]你不知道的CPP11新語法
    就算有了auto類型指示符,遍歷容器/數組每一個元素你還是嫌麻煩?沒事,讓範圍for語句來幫你。原來這麼遍歷容器/數組每一個元素現在這麼寫:注意,範圍for語句只能遍歷每一個元素,所以像遍歷1到10這種操作還是得自己乖乖寫for循環:)。
  • [洛穀日報第29期]OI中可以用到的Linux基礎教程
    Linux基礎教程●前置系統:任意Linux(不用NOI Linux也沒問題,我用的是deepin),如果沒有裝,而且你用的是win10,請看往期的洛穀日報:練習Linux?別急,我們來改一下,右下角有一行字,我們將「純文本」改為c++,制表符寬度按個人喜歡設置,裡面還可以設置自動縮進,後面一個框裡可以選擇顯示行號,高亮當前行,右上方的菜單鍵(三個點)裡面可以設置側邊欄,還有搜索、跳轉行的功能。是不是好看多了?退出之前別忘了點右上方的「保存」。
  • [洛穀日報第53期]淺談一些求近似值的方法
    ,從而保證最優操作流程如下(和二分法類似)附程序:讀者們可以在洛谷P3382(https://www.luogu.org/problemnew/show/P3382)中測試一下(~o ̄3 ̄)~(雖然是三分法模板但也可以用優選法\二分法做哦)關於這個還有一個類似方法:斐波那契法。
  • 五分鐘學算法:什麼是線段樹?
    對於求區間和的問題,前綴和數組 是一個不錯的選擇,構建好前綴和數組後,求一個區間和的話只要前後一減就可以了,如果不算構建數組的時間,那麼每次的操作時間複雜度就是 O(1)。這裡的問題在於 前綴和數組只能解決求區間和的問題,但是其他的區間問題,前綴和數組並不能很好的解決,比如求某段區間上的最大值。
  • 數據結構之php實現線段樹
    今天我們一起學習一種新的樹形結構, 線段樹。在我們平時,可能會遇到一些問題,「給定一個數組, 求出數組某段區間的一些性質」。比如給定一個數組 [-2, 0, 3, -5, 2, -1], 求出區間 【0,4】上元素的和,在這個例子中,答案是-2。你可能會說這麼簡單,直接遍歷一下數組不就好了嗎?是的,這樣是確實最簡單,最粗暴的方式。但是我們想一下,這個時間複雜度是多少?
  • [洛穀日報第59期]我有獨特的騙分技巧
    做過 CF 愚人節題目的 2k 多人應該都知道,愚人節題目 CF409F 的標題就是 000001 ,指的就是 OEIS 裡的 A000001 ,題目是要求輸出該數列的第 n 項。(可見對於這些網站掌握程度的重要性)其它是連結啦,出處啦之類的,大家有興趣可以自己看看,我不多講了。有什麼用?當然是可以打表了!