大殺器 利用劃分樹秒殺區間內第k大的數

2022-07-12 11:18:12 字數 2140 閱讀 9307

最近看了一道題,大概就是給出乙個序列,不斷詢問其子區間內第k大的數,下面是個截圖

繞了一圈沒找到中文版題目,if(你是大佬) then 去看截圖;else這是一道很簡單的模板題,怎麼解決呢?小編最初想到的是打暴力,正所謂暴力出奇蹟,說不定可以成功,反正不會優化,先試試吧,直接把規定區間[l,r]排一次序了,然後在查詢一遍第k大的數,但是毫無疑問,絕對會超時,怎樣能減少時間複雜度呢?這是就請出了二分。

二分運用了分治的思想,不斷將子區間分成兩半,直到找到第k大的數,雖然很高效,但是面對這道題的多次詢問,二分也只能表示手軟,仍然過不了這道題。但是如果換成了線段樹的結構,效率則會快很多,線段樹看起來也用了分治的思想,把原來的整個序列都大約相等的長度分到左子樹和右子樹,不斷分下去,直到全部分成葉子節點,在逐次確定下一層第k大的數在左子樹還是右子樹,雖然這種樹能成功ac,但是並不是最優的,做題不能只講求ac,下面就來講一講線段樹的公升級版——劃分樹。

什麼是劃分樹?

劃分樹是一種基於線段樹的資料結構,也利用了分治的思想,卻比線段樹高效很多,這是為什麼?因為劃分樹又多了乙個性質:在劃分時不是隨意劃分,也不是排序後直接劃分(因為這樣會破壞原有結構),而是排序後仍保持原來的相對順序再分到左右子樹。

具體實現方法:

整個過程分為建樹和查詢兩個階段:

//copy來的圖

1)建樹:首先定義乙個陣列tree[30][1000]第乙個維度表示層數,第二個維度表示這一層第i個數的值,用來表示這棵劃分樹,然後定義sorted[1000]陣列,用來儲存排序好的原序列,然後記錄每一層前i個數有多少進入了下一層的左子樹,存在toleft[30][1000]陣列中,在建樹中沒用,但記錄下來對查詢時有用,用分治的思想分配左子樹和右子樹,將不大於中間值mid的數分配到左子樹中去,大於中間值的分配到右子樹中去,但有時為了左右盡可能個數相等,要把等於中間值的數兩邊都分配,於是定義same來儲存多少等於中間值的數進入左子樹,分配完畢後再遞迴分配左子樹和右子樹。身為遞迴,怎麼也要有個出口吧,遞迴到葉子節點時就返回,即if(l==r) return;

2)查詢:按照之前儲存下的進入下一層左子樹個數的陣列toleft,可以計算出區間內第k大的數在當前節點的左子樹還是右子樹,並計算出下一層相應子樹的左右邊界,然後遞迴相應子樹,同上,遞迴出口也是到達葉子節點時返回。詳見注釋……

廢話不多說,**呈上:

#include#include

using

namespace

std;

int tree[30][1000],sorted[1000],toleft[30][1000],n,m,ans;//

tree和toleft的兩個維度分別儲存深度和序列,sorted儲存的是排序好的序列

void buildtree(int l,int r,int dep)//

構建劃分樹

else tree[dep+1][rpos++]=tree[dep][i];//

剩下的分配到右子區間

toleft[dep][i]=toleft[dep][l-1]+lpos-l;//

toleft陣列記錄這一層前i個數有多少個進入下一層的左子區間,查詢時有用

} buildtree(l,mid,dep+1);//

構建左子區間(左子樹)

buildtree(mid+1,r,dep+1);//

構建右子區間(右子樹)

}int search(int l,int r,int l,int r,int dep,int k)//

查詢第k大的數

else

//在右子樹

}int

main()

sort(sorted+1,sorted+n+1);//

為sorted陣列排序

buildtree(1,n,0);//

建樹 int

a,b,c;

for(int i=1;i<=m;i++)

return0;

}

//額~好像忘了改scanf和printf了,沒過別怪我……

靜態區間第k大(劃分樹)

poj 2104為例 經典劃分樹問題 思想 利用快速排序思想,include include using namespace std const int maxn 100010 int tree 20 maxn 每層每個位置的值 int sorted maxn 排好序的陣列,方便尋找中值 int l...

快樂找到區間第K大 劃分樹

劃分樹,顧名思義就是將乙個序列,劃分成很多小部分。其作用就是可以快樂地找到給定區間第k大的數。其實使用歸併樹和快排也都可以找到區間內第k大的數,但其效率都不如劃分樹要好,快排過的時間複雜度o n x m 而劃分樹是o n x logn 劃分樹原理 1.建樹 根結點就是原序列,左孩子儲存父結點所有元素...

區間第k大(主席樹)

學了一下主席樹模板題,當初看了網上的主席樹講解都沒有看懂,後面看了嗶哩嗶哩的uestc的主席樹,終於看懂了思想。每次更新的複雜度都為logn。每次更新的話就是對要更新的點路徑上的點重新更加乙個,然後進行對沒有影響的那些進行連邊。然後用乙個root記錄每乙個線段樹的根節點下標。include incl...