樹狀陣列入門及例題題解(三) 區間最值

2021-10-02 05:46:52 字數 3180 閱讀 3282

當會用樹狀陣列求區間和了之後 將陣列的意義稍微改變 將**稍作修改 便可以得到用樹狀陣列求區間最值的**

本篇部落格對於求區間最值 以求區間最大值為例 讀者可以讀完之後 嘗試寫一寫求區間最小值的**

在以下的討論中

a[i]代表原區間的元素

c[i]代表樹狀陣列求區間字首和的元素

h[i]代表求區間最大值的元素

一些關鍵的說明 使用樹狀陣列進行區間求和 以及lowbit()函式執行的原理詳見部落格

樹狀陣列入門及例題講解(一)——區間求和及單點修改

在進行區間求和的**中

c陣列元素 c[i] 代表的是區間中的 第 i-lowbit(i)+1 個元素 到 第 i 個元素的和

要求區間最值

我們相類似的建立乙個陣列 h[maxn] 其中

陣列元素 h[i] 代表的是 區間中的 第 i-lowbit(i)+1 個元素 到 第 i 個元素的最大值

那麼對於單點修改 當然仍然與 區間求和的單點修改相差不大 只是不再求和 而是進行取最大值的操作

乙個錯誤**

void

update

(int position,

int value)

}

這個**為什麼錯呢

因為如果假設改動的這個點正好是在這個區間裡面取得最大值的點

而改變之後 使得其不再是最大值了 那程式就出錯了

所以這裡需要對這個**進行修改

乙個比較簡單的想法就是 因為這個點的變動而導致最大值改變的區間 也就是h陣列中的 包含有a[i] 這個元素的所有 h[i] 元素 我們先將其歸零 然後再從頭到尾再來一遍

即如下**:

void

update

(int position,

int value)

}

那現在 顯然複雜度達到了o(nlogn) 複雜度太高 已經體現不出樹狀陣列的優勢了 所以要對這個演算法進行優化

先來看看單點修改操作的目的:

改變某乙個位置的元素 的值 然後更新 h陣列中的區間最大值

而在前面也已經說了

對於 h[i] 這個元素而言 它所表示的是 i-lowbit(i)+1 到 i 的這個區間的元素的最大值

那麼自然 我們改變 a[i] 這個元素 對於 h[1] h[2] ……h[i-1] 這些元素都是沒有影響的

那麼此時可以有乙個大膽的想法 要改變 h[i] 這個東西

因為前面的元素 在我們進行單點修改操作時 是不受影響的 所以我們可以先求出

i-lowbit(i)+1 到 i-1 這個區間內的最大值 然後再與 a[i] 這個改變了的元素比較

從而得出 i -lowbit(i)+1 到 i 這個區間的最大值 也就是 h[i] 這個元素的值就被我們求出來了

於是 現在的問題就轉變為 縮小範圍 找到 i-lowbit(i)+1 到 i-1 這個區間內的最大值

對於這個過程 舉個例子來說:

假設 i = …… 010000

那麼首先就肯定要從 i-1 開始查出這個最大值

i-1 = ……001111

於是 h[i-1] 所表示的區間最大值的範圍就是 ……001111 ~ ……001111 (這裡正好就只包括了他自己)

對於 這個大區間 i-lowbit(i)+1 到 i-1 已經找到了 i-1 的最大值

那麼接下來 範圍縮小 只需要尋找 i-lowbit(i)+1 到 i-2 這個 區間內的最大值了

可知 i-2 =……001110

於是 h[i-2] 所表示的區間最大值就是 ……001101 ~ ……001110 這個區間內的最大值

接下來 要求的就是 ……~ ……001100 的最大值

……我們將這些需要訪問的區間的右端點 (也就是訪問的h陣列中的元素的索引)拿出來看看

可以發現 我們要比較的就是

a[……010000]

h[……001111]

h[……001110]

h[……001100]

h[……001000]

於是可以總結規律如下(以下均以二進位制的形式表示):

i=……010000

a[……010000]

h[……001111] ……001111=……010000-1 # ——i-1

h[……001110] ……001110=……010000-10 #——i- (1<<1)

h[……001100] ……001100=……010000-100 #——i-(1<<2)

h[……001000] ……001000=……010000-1000 #——i-(1<<3)

直至索引為 (1update

(int position,

int value)

position+

=len;

}}那麼此時的複雜度 顯而易見的 達到了 o( (logn)^2 )

區間最值的求法 也與區間求和的方法不太一樣

因為 如果要求的是 區間 i到j 的最大值 並不能像區間求和那樣簡單的用乙個減法就可以實現 現在似乎乍一看沒有什麼思路 那我就來仔細說明一下:

對於乙個區間 i 到 j 而言 要求這個區間的最大值 就要將 這個區間內的所有元素都進行一次比較

首先定義乙個ans=0

那麼就有兩種方法:

1、這樣來看 如果 目標區間完全包括了h[i] 所代表的 i-lowbit(i)+1 到 i 的這個區間那麼就很簡單 直接把 h[i] 與 ans比較一下就可以了 這樣就縮小了區間範圍

2、但是有時候可能運氣沒有這麼好 並沒有只包含了一部分 那雖然包含了一部分 但是依然會有區間外的元素對這個最大值產生的影響 所以不可用 就把 ans 與 a[i] 比較 然後依然可以縮小區間範圍

於是把這個過程寫成程式就可以了,**如下:

int

find_max

(int left,

int right)

}return ans;

}

以上就是使用樹狀陣列求最值的全部內容了 讀者可以再次嘗試一下 用樹狀陣列求區間最小值。

樹狀陣列入門及例題講解(四)——i hate it

樹狀DP入門例題

p1352 沒有上司的舞會 沒有上司的舞會 p1352 題意 有一場舞會,每乙個人都有乙個快樂值,但不能和上司同時出現在舞會中,問這個舞會的快樂值最大為多少 解法 樹狀dp include using namespace std define maxn 100000 struct nodeedge ...

樹狀陣列部分例題

poj 1037 給出n個星星的座標,如果乙個星星的左下方 包含正左和正下 有k顆星星,就說這顆星星是k級的,統計每個等級有多少個點。這題可用樹狀陣列,對於每個星星按y座標從小到大排序,相同y座標按x座標從小到大排序 題目中資料已經有序 輸入順序已排好序,那麼只要依次統計星星i之前x座標小於等於i....

樹狀陣列例題1

如題,已知乙個數列,你需要進行下面兩種操作 1.將某乙個數加上x 2.求出某區間每乙個數的和 第一行包含兩個整數n m,分別表示該數列數字的個數和操作的總個數。第二行包含n個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。接下來m行每行包含3個整數,表示乙個操作,具體如下 操作1 格式 1 ...