RMQ问题
RMQ问题:
区间最小值问题(Range Mininum Query,RMQ)指的是这样一类问题:给出长度为n的序列,支持一个查询操作:Query(x,y),表示询问区间[x,y]的最小值。
暴力查询需要O(n)的时间,效率太低了,线段树也需要O(logn)的时间,Tarjan他老人家又出来了,提出了个Sparse-Table算法,预处理需要O(nlogn),但查询只要O(1)
算法:
我们令D[i][j]表示以i开头,长度为2^j的序列最小值,
不难发现整个序列只有nlogn个元素,
由递推D[i][j]=min(D[i][j-1],D[i+2^(j-1)][j-1]) (即将一个序列一分为二)
可以在O(nlogn)时间内求出整个序列。
查询操作可以这样,我们找到一个长度为2^k的区间,使它不小于原区间一半,这样,以元素x开头的序列与以y元素结尾的两个序列即覆盖了整个查询区间(而计算k差不多可以看成常数时间)
然而上述理论都是在不需要修改操作的情况下进行的,如果需要支持操作,还是乖乖用线段树吧……
另外可以预处理出所有区间长度对应的K值,时间会减少很多!很多!多!多……
代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define rep(i,x,y) for (int i=x;i<=y;i++)
#define dep(i,y,x) for (int i=y;i>=x;i--)
using namespace std;
const int maxn=100000+10,maxw=20;
int n,m,x,y,a[maxn+2],d[maxn+2][maxw+2];
void RMQ_init()
{
rep(i,1,n) d[i][0]=a[i];
for (int j=1;(1<<j)<=n;j++)
for (int i=1;(i+(1<<j)-1)<=n;i++)
d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
}
int RMQ_Query(int x,int y)
{
int k=0;
while ((1<<(k+1))<=(y-x+1)) k++;
return min(d[x][k],d[y-(1<<k)+1][k]);
}
int main()
{
scanf("%d%d",&n,&m);
rep(i,1,n) scanf("%d",&a[i]);
RMQ_init();
rep(i,1,m)
{
scanf("%d%d",&x,&y);
printf("%d\n",RMQ_Query(x,y));
}
return 0;
}

浙公网安备 33010602011771号