RMQ问题就是求解区间内最大值或最小值的算法,没什么好说的ST算法也就是模板题
大佬录制的学习视频:RMQ讲解
ST算法流程
1.预处理
/*RMQ问题的预处理*/
void RMQ(int num)///预处理->O(nlogn)
{
for(int j=1;j<20;j++)///2^j代表区间的最大长度位置的坐标
{
for(int i=1;i<=num;i++)///所访问数组的长度
if(i+(1<<j)-<=m)
{
maxsum[i][j]=max(maxsum[i][j-1],maxsum[i+(1<<(j-1))][j-1]);
minsum[i][j]=min(minsum[i][j-1],minsum[i+(1<<(j-1))][j-1]);
}
}
}
/*注意i,j代表的内层外层*/
边界条件f[i][0]=a[i]
2.查询:
求区间【x,y】最大值得表达式:
k=log (y-x+1)以2为底;///此函数效率不高,可用while(1<<(k+1)<y-x+1) k++;求k
ans=max(f[x][k],f[y-(2^k) +1][k]
例题:
士兵杀敌(三)
时间限制:2000 ms | 内存限制:65535 KB
难度:5
描述
南将军统率着N个士兵,士兵分别编号为1~N,南将军经常爱拿某一段编号内杀敌数最高的人与杀敌数最低的人进行比较,计算出两个人的杀敌数差值,用这种方法一方面能鼓舞杀敌数高的人,另一方面也算是批评杀敌数低的人,起到了很好的效果。
所以,南将军经常问军师小工第i号士兵到第j号士兵中,杀敌数最高的人与杀敌数最低的人之间军功差值是多少。
现在,请你写一个程序,帮小工回答南将军每次的询问吧。
注意,南将军可能询问很多次。
输入
只有一组测试数据
第一行是两个整数N,Q,其中N表示士兵的总数。Q表示南将军询问的次数。(1<N<=100000,1<Q<=1000000)
随后的一行有N个整数Vi(0<=Vi<100000000),分别表示每个人的杀敌数。
再之后的Q行,每行有两个正正数m,n,表示南将军询问的是第m号士兵到第n号士兵。
输出
对于每次询问,输出第m号士兵到第n号士兵之间所有士兵杀敌数的最大值与最小值的差。
样例输入
5 2
1 2 6 9 3
1 2
2 4
样例输出
1
7
代码:
/*此代码未经检验,样例已过,第一道RMQ问题,理解着敲的*/
#include<bits/stdc++.h>
using namespace std;
int maxsum[100080][21],minsum[100080][21],n;
void RMQ(int num)///预处理->O(nlogn)
{
for(int j=1;j<20;j++)///2^j代表区间的最大长度位置的坐标
{
for(int i=1;i<=num;i++)///所访问数组的长度
if(i+(1<<j)-1<=n)
{
maxsum[i][j]=max(maxsum[i][j-1],maxsum[i+(1<<(j-1))][j-1]);
minsum[i][j]=min(minsum[i][j-1],minsum[i+(1<<(j-1))][j-1]);
}
}
}
/*注意i,j代表的内层外层*/
int solve(int l,int r)///询问的时候分别求出最小值和最大值的差
{
int k=0;
while((1<<(k+1))<r-l+1) k++;///求出k
int k1=max(maxsum[l][k],maxsum[r+1-(1<<k)][k]);
int k2=min(minsum[l][k],minsum[r+1-(1<<k)][k]);
return k1-k2;
}
int main()
{
int q,x,y,Max,Min;
while(scanf("%d%d",&n,&q)!=EOF)
{
for(int i=1;i<=n;i++)
{
scanf("%d",&minsum[i][0]);
maxsum[i][0]=minsum[i][0];
}
RMQ(n);
while(q--)
{
int c1,c2;
scanf("%d%d",&c1,&c2);
printf("%d\n",solve(c1,c2));
}
}
return 0;
}