一.RMQ的定义
RMQ的定义:
RMQ是询问某个区间内的最值,主要以ST表的方式实现。
二.ST表
ST表(Sparse Table,稀疏表)是一种数据结构,用来解决区间内的一些问题(比如可以求区间最小值、区间最大值,区间GCD),ST表采用的倍增的思想,我们在使用ST表的时候,可以做到O(nlogn)的时间建表,做到用O(1)的时间去查询。
优点:ST表运行效率高。树状图支持求改操作。
缺点:ST表无法进行修改。树状图运行效率低。
三.模板题
思路
这一题是需要用到最大值和最小值两个RMQ,求出来后再相减就是答案。
我们用mx[i][j]来表示以i为起点,往后延伸2^j的这一段区间的最大值。
同样的,用mn[i][j]来表示以i为起点,往后延伸2^j的这一段区间的最小值。
预处理代码(状态转移方程)
那么状态转移方程怎么写呢?
以j为起点的往后延伸2^i的这一段区间可以被分为两部分:
第一部分是以j为起点的往后延伸2^(i-1)的这一段区间
第二部分是以j+2^(i-1)为起点的往后延伸2^(i-1)的这一段区间
例如:
懂了吗?
预处理代码:
void qpzy()
{
int x=log2(n);
for(int i=1;i<=x;i++)
{
for(int j=1;j+a[i]-1<=n;j++) //a[i]是指2^i
{
mx[j][i]=max(mx[j][i-1],mx[j+a[i-1]][i-1]);
mn[j][i]=min(mn[j][i-1],mn[j+a[i-1]][i-1]);
}
}
return;
}
查询代码
接下来就是查询代码了:
我们可以写一个函数来完成。从外面导入两个数字b和c,代表起始值和结束值。
和预处理一样,也是分为两段来求max和min(具体看代码,不会的可以按照代码模拟一下,其实不是很难,要多模拟你就能懂)。
int zzxsb(int b,int c)
{
int x=log2(c-b+1);
int t1=max(mx[b][x],mx[c-a[x]+1][x]);
int t2=min(mn[b][x],mn[c-a[x]+1][x]);
return t1-t2; //最大和最小两个相减
}
整体代码:
#include <bits/stdc++.h>
using namespace std;
int n;
int mx[50001][21],mn[50001][21],a[20],q;
void qpzy()
{
int x=log2(n);
for(int i=1;i<=x;i++)
{
for(int j=1;j+a[i]-1<=n;j++)
{
mx[j][i]=max(mx[j][i-1],mx[j+a[i-1]][i-1]);
mn[j][i]=min(mn[j][i-1],mn[j+a[i-1]][i-1]);
}
}
return;
}
int zzxsb(int b,int c)
{
int x=log2(c-b+1);
int t1=max(mx[b][x],mx[c-a[x]+1][x]);
int t2=min(mn[b][x],mn[c-a[x]+1][x]);
return t1-t2;
}
int main()
{
scanf("%d%d",&n,&q);
a[0]=1;
for(int i=1;i<=15;i++)a[i]=a[i-1]*2;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
mx[i][0]=mn[i][0]=x; //往后走2^0步就是它自己
}
qpzy();
while(q--)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",zzxsb(x,y));
}
return 0;
}
四.练习题
思路
这一题跟上一题差不多,只需去掉mn[]数组和将max()改成__gcd()就行了。
代码
#include <bits/stdc++.h>
using namespace std;
int n;
int mx[50001][21],a[20],q;
void qpzy()
{
int x=log2(n);
for(int i=1;i<=x;i++)
{
for(int j=1;j+a[i]-1<=n;j++)
{
mx[j][i]=__gcd(mx[j][i-1],mx[j+a[i-1]][i-1]);
}
}
return;
}
int zzxsb(int b,int c)
{
int x=log2(c-b+1);
int t1=__gcd(mx[b][x],mx[c-a[x]+1][x]);
return t1;
}
int main()
{
scanf("%d%d",&n,&q);
a[0]=1;
for(int i=1;i<=15;i++)a[i]=a[i-1]*2;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
mx[i][0]=x;
}
qpzy();
while(q--)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",zzxsb(x,y));
}
return 0;
}