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;
}

posted @ 2016-06-02 19:13  Krew  阅读(173)  评论(0)    收藏  举报