剛學了劃分樹,徹底蛋疼了。。。。。。。
怕忘了,先總結一下
注意:本文中第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,所站過格...