###### tags: `經典問題`
# RMQ 問題
## 問題敘述
對於一個長度為 $n$ 的 array,可以實行以下兩種操作。
* $update(x, y)$:
將 array 的第 $x$ 個元素值更新成 $y$。
* $query(l, r)$:
詢問 array 在 $[l, r]$ 這個範圍內的最大值或最小值是多少。
## Segment Tree
線段樹應該是拿來做 RMQ 問題的常用手段了,build 的時間複雜度 $O(n*logn)$,update 與 query 的時間複雜度都是 $O(log(n))$。
要注意線段樹需要 $4n$ 的空間來儲存。
```c++=
vector<int> tree;
// l, r, idx是線段樹的內部變數
int build(int l, int r, int idx, vector<int> &arr){ // 沒有測試過,但大概的code應該是長這樣
if(l == r)
return tree[idx] = arr[l];
int mid = (l+r)/2;
int l_result = build(l, mid, idx*2+1, arr);
int r_result = build(mid+1, r, idx*2+2, arr);
return tree[idx] = max(l_result, r_result);
}
int update(int l, int r, int idx, int x, int y){ // 將arr[x]改成y
if(l == r)
return tree[idx] = y;
int mid = (l+r)/2;
if(x<=mid)
return tree[idx] = max(update(l, mid, idx*2+1, x, y), tree[idx*2+2]);
else return tree[idx] = max(tree[idx*2+1], update(mid+1, r, idx*2+2, x, y));
}
int query(int l, int r, int L, int R, int idx){ // 找[L, R]中的最大值
if(L == l && R == r)
return tree[idx];
int mid = (l+r)/2;
if(R<=mid)
return query(l, mid, L, R, idx*2+1);
else if(L>mid)
return query(mid+1, r, L, R, idx*2+2);
else {
return max(query(l, mid, L, mid, idx*2+1), query(mid+1, r, mid+1, R, idx*2+2));
}
}
```
## Binary Indexed Tree (Fenwick Tree)
根據[這篇](https://hackmd.io/@wiwiho/cp-note/%2F%40wiwiho%2FCPN-binary-indexed-tree)提到的想法,如果要用 BIT 解決 RMQ 問題,那 update 值的時候,如果要做最大值,那值就只能被改大,做最小值,那值就只能被改小。
這個方法 update 與 query 的時間複雜度應該都是 $O(log(n))$。
要注意 BIT 需要 $n$ 的空間來儲存。
```c++=
vector<int> BIT, arr; // 1-indexed
// 只能將值改大,不可改小
void update(int idx, int val){ // update arr[idx] = val
arr[idx] = val;
for(int i=idx;i<BIT.size();i+=i&-i)
BIT[i] = max(BIT[i], val);
}
int query(int l, int r){ // query [l, r] 之間的最大值
int ans = -1;
for(int i=r;i>=l;){
if(i - (i&-i) < l){
ans = max(ans, arr[i]);
i--;
}
else {
ans = max(ans, BIT[i]);
i-=i&-i;
}
}
return ans;
}
```