RMQ问题
RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干次询问RMQ(i,j),返回数列A中下标在区间[i,j]中的最小/大值。
ST算法
ST(Sparse Table,稀疏表,ST表)是用来解决RMQ问题的一种算法,预处理复杂度O(nlogn),查询复杂度O(1),不支持在线查询。
构造
ST表的构造采用了动态规划的思想,
状态表示:
,即从j到j+2^i-1的最大(或最小)值。
初始状态:
状态转移:
,一共
个状态,每次转移复杂度
,总构造复杂度
查询
我们已经知道以任意一点开始了长度为2的幂次的区间最大值,如果要求长度为L的[x,y]区间内的最大值,就可以求[x,x+k],[y-k+1,y]这两个区间最大值的最大值,其中k是小于L的最大2的幂次。
即
log函数可以预处理一个数组,每次查询复杂度
代码
学习了一些C++类的知识,静态成员必须在类外定义及初始化,在类中只是声明,且在类外用双冒号表示这是一个类的静态成员。
因为不会写模板类,所以额外用了一个变量表示求最大还是最小,0表示求最小值,1表示求最大值。如果哪位巨巨有更好的方法请教教我。
另外,vector初始化第一个参数表示大小,第二个表示初始值,所以二维vector的初始化方式:vector<vector<int>> vvi(outsize, vector<int>(insize))
class SpraseTable
{
static vector<int> Log;
static void init()
{
Log[0] = -1;
for(int i = 1; i < M; i++)
Log[i] = Log[i / 2] + 1;
}
int n, type, t;
vector<vector<int>> table;
inline int fun(int a, int b)
{
return (a < b)^type ? a : b;
}
public:
SpraseTable(vector<int> &src, int _type) : n(src.size() - 1), type(_type)
{
if(!Log[0]) init();
table = vector<vector<int>>(Log[n] + 1, vector<int>(n + 1));
for(int i = 1; i <= n; i++)
table[0][i] = src[i];
for(int i = 1; i <= Log[n]; i++)
for(int j = 1; j <= n; j++)
if(j + (1 << i) - 1 <= n)
table[i][j] = fun(table[i-1][j], table[i-1][j+(1<<(i-1))]);
}
inline int query(int x, int y)
{
t = Log[y - x + 1];
return fun(table[t][x], table[t][y - (1 << t) + 1]);
}
};vector<int> SpraseTable::Log(M);
其他
ST表蕴含了倍增的思想,下次再细学。
ST表维护的运算需要满足什么性质?首先肯定是半群上的,然后为什么这种做法不能用来求区间和?