啊,真是太惨了,刚刚做一道RMQ模板题,发现我以前打的倍增模板是错的,于是我就水一下RMQ的倍增算法。。。全当复习
RMQ(区间最值问题),给出一列数,有Q个询问,每次询问 l~r 之间的最小值。
显然,每次暴力循环找最小值是不够快的,所以需要一个算法或数据结构实现快速查询区间最值。
JB:"国际惯例,先放题目"
题目描述:有N头牛,第i头牛高度为Hi。有Q个询问,第A头牛到第B头牛之间最大高度与最小高度的差。
暴力显然是跑不过去的,所以引进RMQ的倍增算法(Sparse-Table),它可以实现预处理O(nlogn),查询O(1),而且这个算法很好写。
mn[i][j]表示从i开始,长度为2^j的一段元素中的最小值,则可以mn[i][j]可以用递推的方式计算:mn[i][j]=min(mn[i][j-1] , mn[i+2^(j-1)][j-1])
要注意2^j≤n,所以mn数组元素个数不超过nlogn,代码如下:
void rmq_init(){ int i,j,t; for(j=1;j<=n;j++)mn[j][0]=h[j]; int m=floor(log((double)n)/log(2.0)); for(i=1;i<=m;i++){ for(j=1;j<=n;j++){ t=j+(1<<(i-1)); if(t<=n)mn[j][i]=min(mn[j][i-1],mn[t][i-1]); else mn[j][i]=mn[j][i-1]; } } }
其中第一个for循环中i<=m与(1<<i)<=n等价
题目中要求的是输出区间最大值与最小值的差,所以就是区间最大值-区间最小值,最大值的求法类似。
好了,现在mn数组已经预处理好了,那怎么查询呢,其实很简单,令m为满足2^m≤R-L+1的最大整数,则以L开头、以R结尾的两个长度为2^m的区间合起来就覆盖了查询区域[L,R]。
查询代码如下:
int rmq(int l,int r){ int m=floor(log((double)(r-l+1))/log(2.0)); return min(mn[l][m],mn[r-(1<<m)+1][m]); }
至此,上面那题已经解决了,给出代码就当存个模板。。
#include<cstring> #include<cmath> #include<cstdio> #define max(a,b) (a>b?a:b) #define min(a,b) (a<b?a:b) using namespace std; const int maxn=50001; int h[maxn]; int mx[maxn][16],mn[maxn][16]; int n,q; inline int read() { char ch;int a=0;bool f=false; ch=getchar();while((ch<'0'||ch>'9')&&(ch!='-'))ch=getchar(); if(ch!='-')a*=10,a+=ch-'0';else f=true; while(((ch=getchar())>='0')&&(ch<='9'))a*=10,a+=ch-'0'; if(f)a=-a; return a; } void rmq_init(){ int i,j,t; for(j=1;j<=n;j++) mx[j][0]=mn[j][0]=h[j]; int m=floor(log((double)n)/log(2.0)); for(i=1;i<=m;i++){ for(j=1;j<=n;j++){ t=j+(1<<(i-1)); if(t<=n)mx[j][i]=max(mx[j][i-1],mx[t][i-1]); else mx[j][i]=mx[j][i-1]; } } for(i=1;i<=m;i++){ for(j=1;j<=n;j++){ t=j+(1<<(i-1)); if(t<=n)mn[j][i]=min(mn[j][i-1],mn[t][i-1]); else mn[j][i]=mn[j][i-1]; } } } int rmq(int l,int r){ int m=floor(log((double)(r-l+1))/log(2.0)); int a=max(mx[l][m],mx[r-(1<<m)+1][m]); int b=min(mn[l][m],mn[r-(1<<m)+1][m]); return a-b; } int main(){ // freopen("lineup.in","r",stdin); // freopen("lineup.out","w",stdout); int i,l,r; n=read(),q=read(); for(i=1;i<=n;i++)h[i]=read(); rmq_init(); for(i=0;i<q;i++){ l=read(),r=read(); printf("%d\n",rmq(l,r)); } }
loading...