清北學堂(2019 4 29 ) part 2

2022-04-12 03:02:03 字數 3248 閱讀 3732

主要內容資料結構:

1.二叉搜尋樹

一棵二叉樹,對於包括根節點在內的節點,所有該節點左兒子比此節點小,所有該節點右兒子比該節點大,(感覺好像二分...)

每個節點包含乙個指向父親的指標,和兩個指向兒子的指標。如果沒有則為空。每個節點還包含乙個key值,代表他本身這個點的權值

常用操作:

插入乙個數,刪除乙個數,詢問最大/最小值,詢問第k大值。

插入操作:

現在我們要插入乙個權值為x的節點。

為了方便,我們插入的方式要能不改變之前整棵樹的形態。

首先找到根,比較一下key[root]和x,如果key[root] < x,節點應該插在root右側,否則再左側。看看root有沒有右兒子,如果沒有,那麼直接把root的右兒子賦成x就完事了。

否則,為了不改變樹的形態,我們要去右兒子所在的子樹裡繼續這一操作,直到可以插入為止。

刪除操作:

要刪掉乙個權值,首先要知道這個點在哪。

從root開始,像是插入一樣找權值為x的點在哪。

定義x的後繼y,是x右子樹中所有點裡,權值最小的點。

找這個點可以x先走一次右兒子,再不停走左兒子。

如果y是x的右兒子,那麼直接把y的左兒子賦成原來x的左兒子,然後用y代替x的位置。

找第k大的數:

對每個節點在多記乙個size[x]表示x這個節點子樹裡節點的個數。

從根開始,如果右子樹的size ≥ k,就說明第k大值在右側,往右邊走。

如果右子樹size + 1 = k,那麼說明當前這個點就是第k大值。

否則,把k減去右子樹size + 1,然後遞迴到左子樹繼續操作。

那麼為什麼呢?

證明:由二叉搜尋樹性質可得,左邊字數一定比其父節點小,右邊則反之

看看正在處理的點的右子樹根節點,對於其「size」,即節點個數,如果大於k,說明要找的樹(或位址)肯定在右邊

因為左邊的恆比右邊小,如果此時size>=k,說明右面至少還有k個沒找過的數,我們要找的是第k大的數,並不能允許右邊有多於k個比目標大的數,那麼向右找

當找到當前節點的右子樹根節點size+1=k時,說明這個數底下(包括自身)有k-1個比當前處理節點大的數,那麼當前處理節點就是第k大的

如果沒等找到size+1=k就size說明右邊比當前處理節點大的數並不足k,此時應向左找,找比當前節點小的數,也許時不時地向右子樹找一下,最終找到第k大的數

(我因為仔細讀問題,沒看見第k「大」的數...當第k個(小)的數證的,百思不得其解...)

2.二叉堆

乙隻神仙提前講過,我之前也整過,然而用的優先佇列,因為對就是棵完全二叉樹,(看題目,「二叉」堆吖~)

一直沒有手寫堆,今天試試。

**:

#include//本**致力於維護小根堆

using namespacestd;

int a[100005];

intn;

intsize;

inline void up(intt)

}inline void down(intt)

break;

}if(a[t]<=a[l]&&a[t]<=a[r])break;

else if(a[l]

else

}}inline voidpop()

inline void add(intnow)

intmain()

while(size)

return 0;

}p.s.我為了確保這玩意對,特意又交了一遍快排模板

(我寫這玩意用了一晚上(並不怕笑話...),老師說下課時我差點髒話出口...我發誓這種情況不多見)

3.區間rmq問題

舉個例子(例題):

給出乙個序列,每次詢問區間最大值.

看上去暴力模擬能過吖...

然而n

≤ 100000

, q

≤ 1000000.

所以我們需要一套好演算法,比如這個rmbrmq,

主要思路是將總區間不斷二分,根據這些區間進行計算,如圖:

然後將所需訪問區間拆分,但能保留的盡量大的區間一定要保留,因為大區間問問可以表示成許多小區間,隻算大區間顯然更快

延遲更新:

資訊更新時,未必要真的做徹底的更新,可以只是將應該如何更新記錄下來,等到真正需要查詢準確資訊時,才去更新 足以應付查詢的部分。

在區間增加時,如果要加的區間正好覆蓋乙個節點,則增加其節 點的inc值和sum值,不再往下走.在區間詢問時,還是採取正常的區間分解.

在上述兩種操作中,如果我們到了區間[l, r]還要接著往下走,並且inc非0,說明子區間的資訊是不對的,我們將inc傳送到左兒子和右兒子上,並將inc賦成0,即完成了一次更新.

延遲更新可避免每次都賦值的麻煩情況,可極大省時

4.並查集

等我做出來村村通再整...

先整下按秩合併:

對每個頂點,再多記錄乙個當前整個結構中最深的點到根的深度deepx.

注意到兩個頂點合併時,如果把比較淺的點接到比較深的節點上.

如果兩個點深度不同,那麼新的深度是原來較深的乙個.

只有當兩個點深度相同時,新的深度是原來的深度+1.

注意到乙個深度為x的頂點下面至少有2x個點,所以x至多為log n.

那麼在暴力向上走的時候,要訪問的節點至多只有log個 。

然而路徑壓縮更好...雖有兩種演算法一起用的sao操作,但為了避免一種「玄學錯誤」(???)而並不這麼用(起碼用的肥腸少...)

5.樹及lca問題

在一棵有根樹中,樹上兩點x, y的lca指的是x, y向根方向遇到到第乙個相同的點.

我們記每乙個點到根的距離為deepx.

注意到x, y之間的路徑長度就是deepx + deepy - 2 * deeplca

兩個點到根路徑一定是前面一段不一樣,(匯合)後面都一樣.

注意到lca的深度一定比x, y都要小.

利用deep,把比較深的點往父親跳一格,直到x, y跳到同乙個點上.

這樣做複雜度是o(len).

首先不妨假設deepx < deepy.

為了後續處理起來方便,我們先把x跳到和y一樣深度的地方.

如果x和y已經相同了,就直接退出.

否則,由於x和y到lca的距離相同,倒著列舉步長,如果x, y的第2j個父親不同,就跳上去.這樣,最後兩個點都會跳到離lca距離為1的地方,在跳一步就行了.

時間複雜度o(n log n). 

我..累了(qaq)

清北學堂(2019 4 30 ) part 3

今天總的講些演算法,會了的話.看上去好厲害的樣子 1.老朋友動態規劃dp dp重點 1.邊界條件,開頭不需處理的資料,比如斐波那契數列中的第一二項 2.轉移方程,後面的項需要根據前面幾項求出自身值的方程 等式 套路 1.定狀態,2.寫方程,3.敲 三種用法 1.順著推,2.倒著推,3.記憶化搜尋,舉...

清北學堂 2017 10 01

problem 1.alien input file alien.in output file alien.out time limit 1s memory limit 128m 小y 最近正在接受來自x3 星球的外星人的採訪。在那個星球上,每個人的名字都是乙個正整數。所有在這個星球上的居民都是相互...

清北學堂 2017 10 06

因為是剛聽完課所以想把思路記下來,有一些其實也是一知半解的,如果有dalao可以幫忙講解那真是再感謝不過了。還有為什麼我畫圖這麼醜,哇的一下哭出聲 problem a.最佳進製 如今我們最常用的是十進位制,據說這是因為人有十根手指。但事實上這並不是十分方便,10 只有四個因子 1 2 5 10,像 ...