省選 演算法總結 劃分樹

2021-06-13 11:35:21 字數 2861 閱讀 1649

剛學了劃分樹,徹底蛋疼了。。。。。。。

怕忘了,先總結一下

注意:本文中第k大是指從小到大排序後的第k個數,如下表

原陣列2 1 3 2 3

排序後1 2 2 3 3

k=1k=2

k=3k=4

k=5

所以可能會出現重疊的情況

類似歸併排序,把區間每次均分,選乙個基準,小於該基準的到左子樹,大於的到右子樹,然後分別讓左右子樹有序,但是每個區間要保證相對順序,這就可以實現歸併排序

每個區間要保證相對順序

比如7 9 5 4 8 ,我們要把7 5 4三個數分到左子樹,但是到了左子樹後他們的位置必須是7 5 4 ,而不能是4 5 7等等,一句話,相對順序不能變

下面舉個例子看看是怎麼建樹和查詢的

原陣列   [79

5

48]

開始建樹 [75

4][98]

[54][7][8][9]

[4][5]

紅色部分就表示當前區間進入左子樹的數

現在我們需要查詢上面區間[2,5]內(即陰影部分)第2大的數,就會按照以下流程進行

○第二步那裡為什麼不是整個區間[1,3]?

其實原因大家都能想清楚,因為[1,3]並不是以前[2,5]的子區間

○那麼我們該如何確定下一步該查詢的子區間呢?

我們先看區間[1,1]中進入做區間的有1個,因為相對位置不變,所以區間[2,5]進入左區間的數肯定會在那乙個的後面,所以我們就可以根據此確定查詢區間的左界,然後再根據[2,5]有幾個進入左區間來確定右界,這樣整個查詢子區間我們就確定下來了

首先我們需要記錄當前節點的值val

然後,根據前面分析,我們需要記錄當前節點所在區間有多少個進入了左子樹left

想想線段樹,我們是在每個節點建立附加資訊,那麼這裡是不是要在每個節點建立乙個val,left以及乙個陣列呢?

再看看前面那個例子,每個分出來的區間恰好是不重疊而且接上的,那麼我們就可以換一種思路,分層建立資訊,分幾層?前面說過,我們的區間是均分的,所以最多log2n層!n是多少?maxlongint?怎麼可能!就算是線性表也存不下!所以這個n一般只會達到1000000左右,所以層數log2n= 19.9315685693,所以一般情況20層就夠了,如果不放心,每次算一下,然後在記憶體限制允許的情況下稍微多開一點,就ok

所以儲存結構只需如下就行

struct parti_treeval[20][n];

前面已經把大致過程說了,但是還漏了一中難處理的情況,重複!比如 1 3 3 3 3 3 3 3 ,很明顯,基準應該選3,那麼如果我們把≤3的都放在左子樹或者把≥3的放在右子樹那就球了!

所以我們還需要處理重複的放置情況

選基準為sorted[mid],我們維護乙個same,讓它初始值為當前區間的mid-left+1(為什麼等會再說),然後在當前區間的left ~ right內找<sorted[mid]的值,沒找到乙個same-1,現在想想same表示什麼意思?就表示與sorted[mid]相等的值中,我們能放到左子樹的有幾個(好好體會一下)。現在也就知道它的初值為什麼是mid-left+1了,我們也就是先假設分到左子樹的數全部與sorted[mid]相等,然後找到乙個<sorted[mid]的就讓same-1,最後剩下的就是分到左子樹並且與sorted[mid]相等的個數

現在所有情況處理完了,**也就有了

void build_tree(int d,int l,int r)

else

val[d][i].left=val[d][i-1].left+flag;

} build_tree(d+1,l,m);

build_tree(d+1,m+1,r);

}

具體過程前面也已經分析過,關鍵就如計算查詢區間,這裡具體算算

首先我們假設在 [l, r] 中查詢區間 [x, y] 中第k大的數

我們用lx, ly, rx, ry 這四個變數來分別記錄如下資訊

lx[l, x-1]  區間中進入左子樹的個數

ly[x, y]   區間中進入左子樹的個數

rx[l, x-1]  區間中進入右子樹的個數

ry[x, y]   區間中進入右子樹的個數

如果ly≥k,那麼就說明第k大在左子樹,所以下一次我們需要在[l, m] 內查詢區間 [ l-1+lx+1,l-1+lx+ly]【解釋一下l-1是跳到l前乙個位置,然後加上個數,而後面的+1是由於lx表示的是區間[l, x-1] ,並不包含x,而它的下乙個位置就是x,所以+1。這裡為了方便理解沒有把+1-1約掉。後面同理】

反之,如果ly<k,那就說明第k大在右子樹,所以下一次需要在 [m+1, r] 內查詢區間 [m+1-1+rx+1, m+1-1+rx+ry] 【這裡+1-1就不解釋了,跟上面一樣】

漏說了一點,怎麼計算lx, ly, rx, ry呢?我們前面不是維護了left值嗎,所以

lx=val[d-1][x-1].left – val[d-1][l-1].left;

ly=val[d-1][y].left – val[d-1][x-1].left;

rx=(x-1)-l+1-lx;//標記部分為[l, x-1] 的總個數,減去lx即為答案

ry=y-x+1-ly;//標記部分為 [x, y] 的總個數,減去ly即為答案

所以現在也可以出**了

int query(int d,int l,int r,int x,int y,int k)

省選演算法匯集

陣列 鍊錶,雙向鍊錶 佇列,單調佇列,雙端佇列 棧,單調棧 堆並查集與帶權並查集 hash 表 自然溢位 雙hash 樹狀陣列 線段樹,線段樹合併 平衡樹treap 隨機平衡二叉樹 splay 伸展樹 scapegoat tree 替罪羊樹 塊狀陣列,塊狀鍊錶 5.樹套樹 線段樹套線段樹 線段樹套平...

省選 簡單演算法

這裡是一些簡單的演算法模板,沒有編譯過.編譯過的話會做特殊說明 目錄 1.最近公共祖先 2.cdq分治 1.最近公共祖先 namespace lca void dfs int u,int fa if x y return x for int i log 1 i 0 i if f i x f i y ...

8 5省選模擬總結

這次考掛了。其實遇到了舊題,但由於種種原因坑在上面了,於是爆零了 總結一句就是心態問題,早餐很重要 第一題 跳格仔 題目大意 給定n 1個相鄰的格仔,標號為0 n,1 n的格仔都有乙個權值a i 整數,可能為負 給定乙個v,表示一次最多跳到往後第v個格仔裡,求乙個方案,滿足從0到x再回到0,所站過格...