劃分樹學習筆記

2022-05-02 21:45:13 字數 3168 閱讀 5619

今天沒事就去刷以前hdu做過但是沒過的題,前面的題現在還是能過的,做到這題就卡了,傳說中的劃分樹,只聞其名未見其身。然後搜尋了一下劃分樹的資料,擦擦擦,這不就是同快排的原理+線段樹的操作,兩者一融合進化成了劃分樹麼。前面兩個都會,學習起來倍感輕鬆。

建樹過程: 先對區間[1,n]內所有元素進行排序,未排序之前的數列賦值給線段樹的第一層元素(tree[0][i]),然後就是同快排的原理以排序後中間元素為參照,小於它的放在樹下一層的左邊,大於它的放在樹下一層的右邊(劃分樹建樹以中間元素為參照,快排以第一關鍵元素為參照)。然後再開乙個sum陣列,sum[d][i]表示第d層前i個元素有多少個元素小於as[mid](as[mid]為排序後的中間值),這樣一層一層建樹下去最後建完樹後等於對原數列排好了序。

查詢過程: 這裡最關鍵了。在查詢區間[tl,tr]時,往下查詢[tl,tr]左右孩子時,都要對區間[tl,tr]進行更新。

定義兩個數s, ss,  d表示第d層, k(k表示要查詢的第k元素) :

s 表示區間[l,tl]有多少個元素小於as[mid],  s=sum[d][tl-1];

ss 表示區間[tl,tr]有多少個元素小於as[mid], ss=sum[d][tr]-s;

if(ss>=k)  則下一層查詢區間為[l+s,l+s+ss-1];

else  則下一層查詢區間為[mid+1+tl-l-s,mid+1+tr-l-s-ss];

自己懶,不願畫圖多解釋,借用一下小媛姐姐的圖。

分析一下演算法複雜度: 建樹nlogn+查詢mlogn,很強大的說。

題目大意:o(-1)

解題思路:o(-1)

view code

1 #include 2 #include 3 #include 4 #include 5

using

namespace

std;67

const

int maxn=100005;8

int sum[20][maxn], tree[20

][maxn];

9int a[maxn], as[maxn]; ///

原陣列, 排序後陣列

1011

void build(int d, int l, int

r)12

25else

if(tree[d][i]

26else tree[d+1][rp++]=tree[d][i];27}

28if(l==r) return

;29 build(d+1

,l,mid);

30 build(d+1,mid+1

,r);31}

3233

int query(int d, int l, int r, int tl, int tr, int

k)34

4243

intmain()

4455 sort(as+1,as+n+1

);56 build(0,1

,n);

57while(m--)

5864}65

return0;

66 }

題目大意:給你乙個有n個數的數列,然後有m次詢問[l,r],讓你在[l,r]中找乙個數x使得|xi-x|最小(l<=i<=r).

解題思路:初中知識可知,形如|xi-x|最小,那麼必定是找排序後區間的中位數了。區間[l,r]的中位數必定是第k((r-l+2)/2)位。

假設一段區間排序後是x1,x2,x3,x4,x5,x6,x7,x4是中位數,那麼題要求的答案不就是(x4-x1)+(x4-x2)+(x4-x3)+(x5-x4)+(x6-x4)+(x7-x4)=(x5+x6+x7)-(x1+x2+x3)。   

結果分為了比中位數大的數和比中位數小的數,啊哈,這不就是要我們求第k元素麼,這個k值固定了而已(中位數)。這裡需要多開乙個陣列tol來記錄第d層前i個數的和。當查詢到第d層時,如果所求的ss比k大,則結果ans加上被分到右邊的數,向左下層繼續查詢。如果ss比k小,則結果ans減去分到左邊的數,向右下層繼續查詢。

注意區間奇偶判定以及數的範圍。

view code

1 #include 2 #include 3 #include 4 #include 5

using

namespace

std;67

const

int maxn=100005

;8 typedef long

long

lld;

9int sum[20][maxn], tree[20

][maxn];

10 lld tol[20][maxn]; ///

!!!11

int a[maxn], as

[maxn];

1213

void build(int d, int l, int

r)14

22for(int i=l; i<=r; i++)

2331

else

if(tree[d][i]

32else tree[d+1][rp++]=tree[d][i];33}

34if(l==r) return

;35 build(d+1

,l,mid);

36 build(d+1,mid+1

,r);37}

3839 lld query(int d, int l, int r, int tl, int tr, int k, lld &ret)

4052

else

5358}59

60int

main()

6172 sort(as+1,as+n+1

);73 build(0,1

,n);

74 scanf("

%d",&m);

75 printf("

case #%d:\n

",++tcase);

76while(m--)

7785 puts(""

);86}87

return0;

88 }

劃分樹學習 poj 2104

開始學習劃分樹 按照網上的題目大概敲了下 還有些不懂的地方 尤其是查詢 還不是很懂 下次在看懂 下面是這題的 include include includeusing namespace std int const tcnt 100100 struct node tree tcnt 4 int a ...

劃分樹小結

最近學習了一下劃分樹,下面總結一下。我們在求區間最值的時候,一般可以用線段樹解決,但是如果要求區間第k小或者第k大值的話線段樹就有點力不從心了,這是我們可以用劃分樹來解決。劃分樹利用了快速排序的思想,首先是建樹,我們設當前區間的中位數為mid,為了能快速找到區間的中位數,我們一般先對原序列做一次排序...

劃分樹詳解

題目 給出n個數,和m個區間 對於每個區間 l r 輸出這個區間裡面第k大的數。我們來看看劃分樹是怎麼構造的。構造 以2104為例子,舉2 0 6 8 5 1 4 3 9為例子,構造劃分樹是這樣的 上面的內容是用乙個二位陣列val儲存下來的,第一維表示劃分樹的第幾層,第二位表示在這一層中的位置。要注...