RMQ区間の一番の問題


RMQ(Range Minimm/Maximum Query)問題は区間の一番値を求める問題です.もちろんO(n)の(どのように書いてもいいですよね=u=)を書いてもいいです.でも、もし1000万回の値を聞きたいなら、切ってもいいです.この時は、安心して線分樹を書いてもいいです.(前提は間違えないです.)かけられないはずです.でも、ここにはもっと簡単なアルゴリズムがあります.STアルゴリズムです.O(nlogn)の前処理ができます.O(1)は質問ごとに答えます.       STアルゴリズムはどのように実現されているかを見てください.      
       まず、前処理です.一つのDPで解決します.a[i]を設定すると、区間の一番値を要求する数列です.f[i,j]はi番目の数から2^j個の数の中の最大値を表します.例えば、数列3 4 4 4 4 5 6 1 2 9、f[1,0]は1番目の数から、長さは2^0=1の最大値です.実は、この3=1、f=2=2=2、f=2実はa[i]に等しいです.このように、Dpの状態、初期値はすでにあります.残りは状態移行方程式です.f[i,j]を平均的に二段に分けます.(f[i,j]は偶数の数字に違いないので)、iからi+2^(j-1)-1は一段、i+2^1(j-1)からi+2 j-1までを一段に分けます.[i,j]はこの2つの最大値の中の最大値です.そこで、動規方程式F[i,j]=max(F[i,j-1],F[i+2^(j-i),j-1]を得ました.
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];
		}
    }
     次に一番の値を出すには、いい方法があります.O(1)ができますか?それとも分けますか?上記のように区間の最大値を求めます.[2,8]を[2,5]と[5,8]の二つの区間に分けます.この二つの区間の最大値は直接f[2,2]とf[5,2]から得られます.一般的には、区間[r]を2つの長さが2^kの区間に分かれます.f[i,j]の対応が保証されます.直接式を与えます.
k:=trunc(ln(r-l+1)/ln(2);
ans:=max(F[l,k],F[r-2^k+1,k])
int m=floor(log((double)(r-l+1))/log(2.0));
    int a=max(mx[l][m],mx[r-(1<<m)+1][m]);
練習します
/*
pku3264
         ,      i   j              
 rmq  【i,j】           
rmq    :
 ,   
 a[i]          ,f[i,j]    i     2^j       。
    3 2 4 5 6 8 1 2 9 7 ,f[1,0]   1   ,   2^0=1    ,    3   。
f[1,2]=5,f[1,3]=8,f[2,0]=2,f[2,1]=4……       f[i,0]     a[i]。
  ,Dp   、       ,           。
   f[i,j]      (  f[i,j]         ),
 i i+2^(j-1)-1   ,i+2^(j-1) i+2^j-1   (    2^(j-1))。
     ,  i=1,j=3   3,2,4,5   6,8,1,2   。
f[i,j]              。
           F[i,j]=max(F[i,j-1],F[i+2^(j-i),j-1]).
 ,  
           [2,8]    ,      [2,5] [5,8]    ,                  f[2,2] f[5,2]  。       ,     [l,r]       2^k   (   f[i,j]  )。       :
k:=trunc(ln(r-l+1)/ln(2));
ans:=max(F[l,k],F[r-2^k+1,k]);
*/
#include <iostream>
#include <math.h>
#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;

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()
{
	int i,l,r;
	scanf("%d%d",&n,&q);
	for(i=1;i<=n;i++) scanf("%d",&h[i]);
	rmq_init();
	for(i=0;i<q;i++){
		scanf("%d%d",&l,&r);
		printf("%d
",rmq(l,r)); } return 0; }