原题链接:www.luogu.com.cn/problem/P28…
一道非常经典的区间最值问题,对于这种问题,我们可以使用多种方法求解。
首先由于本题没有修改操作,整体为静态区间,所以我们可以考虑使用ST表来维护区间最值。
ST表运用了一种类似于分治的思想,将整个区间拆分为多个长度为 的区间,其中显然的,较大区间的最值可以由组成它的两个较小区间的最值求得,即将一个长度为 的区间拆分为两个长度为 的区间。这种思想的应用很广,例如在线段树、树状数组、Floyd算法优化、背包dp优化、倍增求LCA等等都有出现。
以求区间最大值为例,我们用 表示以 为左端点,长度为 的区间的最大值,则有转移方程:
由此则可以从小区间推到大区间。
那么我们怎么进行查询呢?设查询区间为 ,则我们设 , ,分别查询 和 ,并在其中取最大值,可以发现这刚好覆盖了整个查询区间。
coding:
class sparse_table
{
private:
vector<vector<int>> st;
vector<int> lg;
int type;
public:
sparse_table(const vector<int> &arr, const int type)
{
this->type = type;
int n = arr.size();
int max_log = log2(n) + 1;
st.resize(n + 1, vector<int>(max_log));
lg.resize(n + 1);
lg[1] = 0;
for (int i = 2; i <= n; i++)
lg[i] = lg[i / 2] + 1;
for (int i = 1; i <= n; i++)
st[i][0] = arr[i];
for (int j = 1; j < max_log; j++)
{
for (int i = 1; i + (1 << j) <= n + 1; i++)
st[i][j] = (type == 1 ? max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]) : min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]));
}
}
int query(int l, int r)
{
int len = r - l + 1;
int k = lg[len];
return (type == 1 ? max(st[l][k], st[r - (1 << k) + 1][k]) : min(st[l][k], st[r - (1 << k) + 1][k]));
}
};
有一个小技巧是预处理log值,这样可以减少运算次数,提高效率。
此外,对于区间最值问题,显然线段树是很容易处理的,而且可以满足动态修改和查询。
但如果我们要使用树状数组,应该怎么处理以求得区间最值呢?我们不妨看看,当我们查询区间 时,有以下两种情况:
① ,则我们可以将区间拆成两个部分,一个为 ,一个为 ,不难看出,其中的第二个部分的区间最值即为 ,则此时我们的问题就转化成了求 。
② ,则此时我们直接将区间拆分为 和一个单独的点 ,问题就变成了求 。
然后我们使用递归求解,最终即得答案。
coding:
class fenwick_tree
{
private:
int n;
int type;
vector<int> bit;
vector<int> arr;
public:
fenwick_tree(const int n, const int type)
{
this->n = n;
this->type = type;
bit.resize(n + 1, (type == 1 ? INT_MIN : INT_MAX));
}
int lowbit(int x)
{
return x & (-x);
}
void update(int idx, int val)
{
while (idx <= n)
{
if (type == 1)
bit[idx] = max(bit[idx], val);
else
bit[idx] = min(bit[idx], val);
idx += lowbit(idx);
}
}
void initialize(vector<int> &arr)
{
this->arr = arr;
for (int i = 1; i <= n; i++)
update(i, arr[i]);
}
int query_range(int l, int r)
{
if (r > l)
{
if (type == 1)
{
if (r - lowbit(r) > l)
return max(query_range(l, r - lowbit(r)), bit[r]);
else
return max(query_range(l, r - 1), arr[r]);
}
else
{
if (r - lowbit(r) > l)
return min(query_range(l, r - lowbit(r)), bit[r]);
else
return min(query_range(l, r - 1), arr[r]);
}
}
return arr[r];
}
};
但说实话树状数组其实并不适合求解区间最值问题,在一般情况下总体复杂度是 的,在这个时候使用线段树会更优。