做后缀数组卡了一天,发现要学RMQ。。。
如此一环套一环地学下去,什么时候才能学到后缀自动机QAQ
求区间最值:给出一组数据,给出m组查询,每组查询求一个区间的最值。
线段树查询的复杂度是O(mlogn),RMQ的查询复杂度是O(n)。在RMQ中,只要预处理(复杂度为O(nlogn))后,每个区间都可以O(1)求出最值,这就是在时间上优于线段树的地方。不过相对应地空间付出了代价,线段树只需开4倍空间即可,RMQ每次要开logn倍的空间,空间消耗是线段树的很多倍。
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <algorithm>
#include <queue>
#include <iostream>
#include <assert.h>
#define INF 0x3f3f3f3f
using namespace std;
//区间最小值查询(最大值改改就OK)
//预处理O(nlogn),查询O(n)
const int M = 1e5 + 10;
int Log[M]; //Log[i]表示i这个数在第几层
int rmq[M][20]; //rmq[i][j]表示,从第i位到第i+2^j-1位的最小值(两边都是闭区间),区间长度为2^j
int a[M]; //输入的数组
void rmqinit(int n) //预处理
{
Log[0] = Log[1]= 0;
for (int i = 2; i <= M; i++)
Log[i] = Log[i >> 1] + 1; //i在第几层
for (int i = 1; i <= n; i++)
rmq[i][0] = a[i]; //初始化,每个第i位的最值就是它本身
for (int j = 1; j <= Log[n]; j++) {
int k = 1 << (j - 1);
//i+(1<<j)-1<=n 防止爆空间。卿爷说一般开两倍空间,就可以不写这句话,但这样写节约空间
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
rmq[i][j] = min(rmq[i][j - 1], rmq[i + k][j - 1]);
}
}
}
int rmquery(int a, int b) //查询
{
//查询x-y的最值,找到中间的两个数xx,yy,取x-xx,yy-y两段的最值就OK
//懂原理的话这里就好懂很多
int t = Log[b - a + 1]; //查询的这段的长度在第几层
return min(rmq[a][t], rmq[b - (1 << t) + 1][t]);
}
int main()
{
int n;
while (cin >> n) {
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
rmqinit(n);
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
cout << i << "~" << j << "~" << rmquery(i, j) << endl;
}
}
}
return 0;
}