資料結構 歸併樹 區間第k大

2021-08-15 16:56:44 字數 1435 閱讀 5117

求區間第k大有很多種方法比如可持久化線段樹,分桶法和平方分割 當然,我們還可以用歸併樹;(請原諒蒟蒻才曉得歸併樹,寫的不好)。

我們把數列用線段樹維護起來。線段樹的每個節點都儲存了對應區間排好序後的結果。在這之前我接觸到的線段樹上面儲存的都是數值,而這次有所不同,每個節點儲存的是數列。

如圖(畫的醜,勿噴):

建立線段樹的過程和歸併排序類似,每個節點的數列是其兩個兒子的數列進行排序的結果。建樹的複雜度是o(nlogn);其實這棵樹也就是歸併排序的完整體現。

那麼要找區間第k大,我們通過二分答案來判斷。就像分桶法和平方分割 一樣,只是判定方式有區別。

要找區間內比當前x小的數的個數,我們可以用這棵樹來查詢。

- 如果所給的區間和當前節點的區間沒有交集,返回0;

- 如果所給的區間完全包含了當前區間,那麼用二分查詢的方法來對當前節點上儲存的數列進行查詢(比如可以用upper_bound);

- 否則對兩個兒子進行遞迴計算後求和返回;

由於對於線段樹需要logn個區間,每個區間查詢需要logn次,查詢一次的時間複雜的為o((logn)^2);

所以總的時間複雜度是o(nlogn+m(logn)^3);

#include

#include

#include

#include

#include

using

namespace

std;

#define lch (rt<<1)

#define rch ((rt<<1)+1)

const

int maxn=100005;

int a[maxn],nums[maxn];

vector

data[maxn*4];

int n,m;

void build(int rt,int l,int r)

else

//for(int i=0;i// printf("%d ",data[rt][i]);

//printf(":%d %d %d\n",rt,l,r);

}int check(int i,int j,int x,int rt,int l,int r)

int main()

sort(nums+1,nums+n+1);

build(1,1,n);

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

printf("%d\n",nums[ub]);}}

注:merge函式的作用是:將兩個有序的序列合併為乙個有序的序列。函式引數:merge(first1,last1,first2,last2,result,compare);如果要合併的是vector要事先用resize。

區間第k大(主席樹)

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

主席樹區間第K大

主席樹的實質其實還是一顆線段樹,然後每一次修改都通過上一次的線段樹,來新增新邊,使得每次改變就改變logn個節點,很多節點重複利用,達到節省空間的目的。1.不帶修改的區間第k大。hdu 2665 模板題 1 include2 using namespace std 3 define fopen fr...

區間第k大

問題描述 給定乙個序列,每次詢問序列中第l個數到第r個數中第k大的數是哪個。輸入格式 第一行包含乙個數n,表示序列長度。第二行包含n個正整數,表示給定的序列。第三個包含乙個正整數m,表示詢問個數。接下來m行,每行三個數l,r,k,表示詢問序列從左往右第l個數到第r個數中,從大往小第k大的數是哪個。序...