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]を得ました.
k:=trunc(ln(r-l+1)/ln(2);
ans:=max(F[l,k],F[r-2^k+1,k])
まず、前処理です.一つの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;
}