當會用樹狀陣列求區間和了之後 將陣列的意義稍微改變 將**稍作修改 便可以得到用樹狀陣列求區間最值的**
本篇部落格對於求區間最值 以求區間最大值為例 讀者可以讀完之後 嘗試寫一寫求區間最小值的**
在以下的討論中
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] 元素 我們先將其歸零 然後再從頭到尾再來一遍
即如下**:
那現在 顯然複雜度達到了o(nlogn) 複雜度太高 已經體現不出樹狀陣列的優勢了 所以要對這個演算法進行優化void
update
(int position,
int value)
}
先來看看單點修改操作的目的:
改變某乙個位置的元素 的值 然後更新 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 ...