RMQ-ST 詳細講解 poj3264爲例

首先介紹一下什麼是RMQ。RMQ (Range Minimum/Maximum Query)問題是指求區間最值的問題,這個問題用線段樹同樣可以解決。

線段樹解決方法

ST

ST算法是解決RMQ問題較優的算法,它是基於動態規劃和位運算實現的。它可以通過O(nlogn )的預處理對每次查詢在O(1)時間得出答案,是一個很快速的算法。

我們設dp[i][j]是區間[i,i+2j -1]的最值,對於該區間它總共有2j 個數,所以dp[i][j]實際就是求i和i之後(包括i的)2j 個數的最值。
所以當我們要求解dp[i][j]時我們可以把它分爲兩部分,每部分數的個數均爲2j1 ,即分爲區間[i,i+2j1 -1]的最值(即dp[i][j-1])和區間[i+2j1 ,i+2j -1]的最值(即dp[i+(1<<(j-1))][j-1])。
這樣我們就得出了狀態轉移方程
dp[i][j] = max/min(dp[i][j-1],dp[i+(1<<(j-1))][j-1])

當我們查詢任意區間[l,r]的最值的時候,先計算出該區間有多少個數(len=r-l+1),然後我們把它分爲兩個區間(這兩個區間可以重疊但必須包含[l,r]中所有數),這樣我們直接取這兩個區間的最值即可。
現在關鍵問題就是怎麼分這兩個區間,其實就是計算2k <=len,這樣我們就可以把它分成前2k 個數和後2k 個數,即區間[l,l+2k -1](即dp[l][k])和區間[r-(1<<(k))+1,r](即dp[r-(1<<(k))+1][k])。
所以ans=max/min(dp[l][k],dp[r-(1<<(k))+1][k])

poj3264

題意
給你n個數和q次詢問,(n<=50000,q<=200000),每次詢問區間[l,r]中最大值和最小值的差。

題解
就是裸題,直接維護最大值和最小值即可。

代碼

//#include <bits/stdc++.h>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int maxn = 5e5+5;
int ma[maxn][20],mi[maxn][20];
int n,m;

void ST(int len)
{
    int k = (int)(log((double)len)/log(2.0));
    for(int j=1;j<=k;j++)
    {
        for(int i=1;i+(1<<j)-1<=len;i++)
        {
            ma[i][j] = max(ma[i][j-1],ma[i+(1<<(j-1))][j-1]);
            mi[i][j] = min(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
        }
    }
}

int RMQ(int l,int r)
{
    int k = (int)(log((double)(r-l+1))/log(2.0));
    int ans1 = max(ma[l][k],ma[r-(1<<k)+1][k]);
    int ans2 = min(mi[l][k],mi[r-(1<<k)+1][k]);
    return ans1-ans2;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&ma[i][0]),mi[i][0]=ma[i][0];
    ST(n);
    int l,r;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&l,&r);
        printf("%d\n",RMQ(l,r));
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章