資料結構複習 Updating

2021-07-23 18:48:29 字數 3367 閱讀 5142

發現很多資料結構我都是只寫過一兩次根本不熟練,有些東西的某些應用都不太了解。

感覺藥丸,反正先挖個坑。

upd 2016.11.23 動態開點線段樹及其合併

這裡居然有個坑。

然後我是不是奶了一下,然後我在noip中寫資料結構就掛了。

先來幹一發動態開點線段樹及其合併,講一講基本操作。

思路很簡單,就是假裝有乙個下標範圍非常大(通常是權值範圍1...v)的線段樹,原先的結點數量非常多,那就考慮只建出對應資訊非0的結點。

當然根據線段樹的性質,層數上限還是logv,然後可以發現v變得很大時logv基本不變。

那麼建樹建個根結點就好了o(1),修改如果進入未建出的結點才新建o(logv),比較優秀。

還有就是合併,兩個子樹合併如果一者為空則取另一者,均不為空則遞迴左右子樹。

考慮合併樹a和樹b複雜度,我們發現這取決於遞迴到最優子樹的次數。

顯然我們只會在訪問到a和b的公共結點時遞迴,於是合併複雜度為o(min(a.size,b.size))。

這類似於啟發式合併,容易知道,將若干個size和不超過n的樹,以任意給定順序合併到一起,時間複雜度不會超過o(nlogn)。

於是我們可以支援這樣的一些操作:維護若干個集合,支援加數,合併,求第k大,求排名。建成權值線段樹就好了。

然後我感覺如果這篇博文全部貼源**的話,就會非常長然後就比較難看,所以我扔了ubuntu pastebin。

然後我寫的時候,加數我是寫成建乙個單個數的集合然後再合併進去。

**:然後寫完資料結構肯定要套幾個題目。

bzoj2733 永無鄉

裸題,直接拉板就好了。

**:codevs fib

動態開點線段樹套矩陣乘法。

upd 2016.11.25 樹鏈剖分+線段樹

我原來想把這種東西叫做樹鏈剖分套線段樹,後來想想並不能算作是「套」。

畢竟樹套樹一般都是指樹的每個結點都對應著一棵樹,然而在樹鏈剖分+線段樹里,我們一般都是只建一棵匯流排段樹。

但是總的來說,這和樹套樹的共同點是,將兩種不同的樹形資料結構結合了。

然後樹鏈剖分和線段樹是非常經典的一種結合。

然後實現也很無腦,遍歷的時候我們優先遍歷每個結點的重兒子,為結點打上dfn序。

那麼我們可以發現,一條重鏈上的各店的dfn序是連續的,一棵子樹中的各點的dfn序也是連續的。

根據這個性質,我們先預處理一棵匯流排段樹,按dfn序維護每個結點的資訊以及每個關於dfn序的連續段的資訊。

然後對於鏈操作我們就拆成log條重鏈扔進線段樹複雜度o(log^2n),對於子樹操作我們就直接扔進線段樹複雜度o(logn)。

然後講幾個細節,都是我很久沒寫了於是沒注意到的東西:

1、線段樹建樹時,線段樹中第x個葉結點初始資訊要對應原樹中dfn為x的結點,而不是原樹中編號為x的結點。

2、還是線段樹的各種操作,資訊上傳資訊更新標記下傳標記合併什麼的不要寫錯,因為難點肯定在於線段樹,樹鏈剖分往往只是個小套路。

bzoj2243

也就是線段樹的操作稍微麻煩一點。

**:upd 2016.11.29 主席樹

思路以前的博文有,大概一年前了。

bzoj3123

啟發式合併,然後維護一下倍增陣列和主席樹。

然後那個testcase不是資料組數而是測試點編號!

然後那個testcase不是資料組數而是測試點編號!

然後那個testcase不是資料組數而是測試點編號!

不注意的話就可能會re/tle,所以我的**裡有一堆奇奇怪怪的除錯資訊。

**:upd 2016.12.3 虛樹

開始給你一棵大小為n的樹,每次給出所有結點的乙個大小為m子集,詢問一些可以通過樹形dp等樹上演算法解決的,但只對於子集的一些問題。

通常會約定每次給出的m之和不會超過某個值,也即,你要給出乙個每次時間複雜度沒有因子n的演算法(包含logn當然是允許的)。

既然不能包含n,即不能對整棵樹進行dp,我們考慮只取下其中的若干「關鍵點」,然後將非關鍵的東西縮掉,得到一棵新的等價的樹,就容易處理了。如圖。

我們點集按dfs序排序,然後每兩個相鄰的點的lca和點集中的每個點都是「關鍵點」(要去重)。如果不取下lca,路徑交叉處不好處理。

具體實現過程中,我們需要知道新樹每個結點的父親以及新樹的dfs序,我們可以這樣做:

考慮用乙個棧維護這個新樹中dfs序最大的從根向下的鏈,然後每次加入乙個dfs值遞增的點,考慮新點和鏈底端的點,如果新點不在鏈底端的點的子樹中,那我們就可以不斷彈出鏈底端的點,同時標記這些彈出的點的父親結點就是鏈中的上乙個結點。注意,最後一次彈出時,父親結點應該設為新點和它的lca。如圖。

於是直到新點在鏈底端的點的子樹中,把新點加進去(如果至少彈出一次,而且最後一次彈出求得的lca還未被加入過鏈中,則應先將lca加入鏈底,然後加新點),維護完成。

最後還需要將棧中剩餘元素彈出。同時注意到,如果將彈出的順序反過來,則恰好是新樹的dfs序。

有些時候這棵樹可能並不需要真的建出來,如某些自下向上的樹形dp,直接按照dfs序掃一次同時對父親結點做貢獻即可。

bzoj2286

建完虛樹就好了,dp部分是人都會。

**:bzoj3611

(未完待續)

upd 2016.12.9 線段樹時間分治

對於一些有加入元素和刪除元素的離線問題,我們可以採用一些技巧將其轉化。

考慮乙個元素的加入時間戳和刪除時間戳,將這個時間段扔到一棵以時間戳為下標建立的線段樹上。

於是每個元素被掛在了線段樹的某些結點上,每個詢問被掛在了線段樹的某個葉節點上,乙個詢問會受到該葉結點及其所有祖先結點上的元素的貢獻。

如果一些元素對乙個詢問的貢獻,在元素被分組的情況下可以支援貢獻的快速合併,那麼對每個詢問列舉其每個祖先結點然後合併貢獻即可,這樣就轉化為了不帶修改的問題。

如果不具有上述性質,但是如果在只有加入元素而不刪除元素的情況下資料結構是可撤銷的,即度是每次嚴格而非均攤分析的,也有相應的處理方法。

考慮對這棵線段樹進行遍歷,要在訪問到每個結點時維護該結點及其祖先上的所有元素構成的資料結構,那麼只要在向下遍歷時加入元素而回溯時做撤銷就好了。

如果這些性質都不具備,則可能要考慮其他的轉化方法。

bzoj4311

由於維護動態加點的凸包時,並不容易做到每次嚴格,因此不適用於第二種轉化。

但是當點集被分組時,要找和給定點的點積最大的點,只需要對每組點分別維護乙個凸包,詢問時對每組點分別找一次最大然後取最優組即可,這適用於第一種轉化。

**:

《資料結構梳理》 Updating

陣列 鍊錶arraylist 底層實現動態陣列 大量查詢操作,獲得某處的值,不適合增刪改查 linklist 底層實現鍊錶 適合增刪改查多的時候,不適合大量查詢 stack 棧,後進先出 1.判空 2.長度 3.壓棧 4.出棧 queue 佇列,先進先出 1.判空 2.長度.3入佇列.4.出佇列 棧...

資料結構複習

1 二叉遍歷 先序遍歷 先遍歷根結點,遍歷左子樹,遍歷右子樹 中序遍歷 先遍歷左子樹,遍歷根結點,遍歷右子樹 後序遍歷 先遍歷左子樹,遍歷右子樹,遍歷根結點 例項 遍歷a b c d e f 如 a e f b c d 先序遍歷 a b cd ef 中序遍歷 a b c d e f 後序遍歷 abc...

資料結構複習

個人理解 1 我認為關鍵是如何合理地將資料放到樹形結構裡 放的方式就是中序排序,即遍歷一遍順序與之相同 2 二叉搜尋樹類似於二分,可以與折半搜尋相比較,雖然思想相似,但是他們的時間效能有時不相同 例如 如果防止不當,42放到了左半枝,這樣就會導致如果輸入31開始比對就會產生位置確認不準確的問題 把一...