RMQ問題

ST表是用來解決RMQ(區間最值)問題的算法python

預處理O(nlgn) 查詢O(1) 不支持在線查詢ios

最小值能夠合併但不支持分割算法

好比說咱們知道[1,9]和[6,10]的最小值,咱們能夠知道[1,10]的最小值,但不能知道[6,9]的最小值數組

咱們能夠枚舉以每一個節點爲起點通過k個節點的最值大數據

可是預處理是O(n2),這時候咱們想到了倍增,一種十分巧妙的思想
spa

它能夠在變化規則相同的狀況下加速狀態轉移,咱們每次把k擴大一倍blog

f[i,j]表示[i,i+2j-1]區間內的信息繼承

f[i,j]:=f[i,j-1]∪f[i+2j-1]string


代碼實現
it

 

//初始化
bin[0]=1;
for(int i=1;i<20;i++)
    bin[i]=bin[i-1]*2;//bin[i]表示2的i次方
Log[0]=-1;
for(int i=1;i<=200000;i++)
    Log[i]=Log[i/2]+1;//Log[i]表示log(i)
for(int i=1;i<=n;i++)
    mn[0][i]=a[i];//顯然i到i+2^0-1就i一個位置,那麼最小值等於本身自己的值
for(int i=1;i<=Log[n];i++)
    for(int j=1;j<=n;j++)
        if(j+bin[i]-1<=n)
            mn[i][j]=min(mn[i-1][j],mn[i-1][j+bin[i-1]]);//狀態繼承
//查詢x到y的最小值
int t=Log[y-x+1];
printf("%d\n",min(mn[t][x],mn[t][y-bin[t]+1]));

 

 [例題1]Balanced Lineup

題目描述

天天,農夫 John 的N(1 <= N <= 50,000)頭牛老是按同一序列排隊. 有一天, John 
決定讓一些牛們玩一場飛盤比賽. 他準備找一羣在對列中爲置連續的牛來進行比賽. 
可是爲了不水平懸殊,牛的身高不該該相差太大. 
John 準備了Q (1 <= Q <= 180,000) 個可能的牛的選擇和全部牛的身高 (1 <= 
身高 <= 1,000,000). 他想知道每一組裏面最高和最低的牛的身高差異. 
注意: 在最大數據上, 輸入和輸出將佔用大部分運行時間. 

 

輸入

6 3
1
7
3
4
2
5
1 5
4 6
2 2
 
 

 

輸出

6

3

0

[參考程序]

#include<iostream> 
#include<cstring> 
#include<cstdio> 
#include<climits> 
#include<cmath> 
#include<algorithm> 
using namespace std; 
   
const int N = 50005; 
int FMAX[N][20], FMIN[N][20]; 
   
void RMQ(int n) 
{ 
    for(int j = 1; j != 20; ++j) 
    { 
        for(int i = 1; i <= n; ++i) 
        { 
            if(i + (1 << j) - 1 <= n) 
            { 
                FMAX[i][j] = max(FMAX[i][j - 1], FMAX[i + (1 << (j - 1))][j - 1]); 
                FMIN[i][j] = min(FMIN[i][j - 1], FMIN[i + (1 << (j - 1))][j - 1]); 
            } 
        } 
    } 
} 
   
int main() 
{ 
    int num, query; 
    int a, b; 
    while(scanf("%d %d", &num, &query) != EOF) 
    { 
        for(int i = 1; i <= num; ++i) 
        { 
            scanf("%d", &FMAX[i][0]); 
            FMIN[i][0] = FMAX[i][0]; 
        } 
        RMQ(num); 
        while(query--) 
        { 
            scanf("%d%d", &a, &b); 
            int k = (int)(log(b - a + 1.0) / log(2.0)); 
            int maxsum = max(FMAX[a][k], FMAX[b - (1 << k) + 1][k]); 
            int minsum = min(FMIN[a][k], FMIN[b - (1 << k) + 1][k]); 
            printf("%d\n", maxsum - minsum); 
        } 
    } 
    return 0; 
} 

[例題2] 不同凡響

【題目描述】 
A是某公司的CEO,每月都會有員工把公司的盈利數據送給A,A是個不同凡響的怪人,A不注重盈利仍是虧本,而是喜歡研究「完美序列」:連續的互不相同的序列。A想知道區間[L,R]之間最長的完美序列。 
【輸入格式】 
第一行兩個整數N,M(1<=N,M<=200000),N表示連續N個月,編號爲0到N-1,M表示詢問的次數。第二行N個整數(絕對值不超過106),第i個數表示該公司第i個月的盈利值。接下來M行每行兩個整數L,R(0<=L<=R<=N-1),表示A詢問的區間。 
【輸出格式】 
輸出M行,每行一個整數對應詢問區間內的完美序列的最長長度。 
【樣例輸入】 
9 2 
2 5 4 1 2 3 6 2 4 
0 8 
2 6 
4 8 
【樣例輸出】 


[分析]

本題是RMQ較複雜的問題。用last[value]表示盈利值value最近出現的位置,一開始所有賦爲0。

用st[i]表示以第i個數爲結尾的最長完美序列的起始位置,st[i]=max(st[i-1],last[a[i]]+1)(a[i]表示第i個月的盈利值),用f[i]表示第i個數爲結尾的最長完美序列的長度,顯然f[i]=i-st[i]+1

for i:=1 to n do begin
  read(a[i]);
  st[i]:=max(st[i-1],last[a[i]]+1);
  q[i]:=i-st[i]+1;
  last[a[i]]:=i;
end;

能夠發現st數組單調不減。 
因而對於一個分割點mm有兩種狀況: 
一、mm左邊一部分st[]值<=l-1 
二、mm右邊一部分st[]值>=l 
由於st[]是單調的,因此能夠用二分法。 
左半部分最大值即mm-l,右半部分最大值即q[mm..r]的最大值

uses math;
var
  a,st,q:array[0..200001]of longint;
    last:array[-1000001..1000001]of longint;
    rmq:array[0..200001,0..21]of longint;
    i,j,ans,n,m,l,r,mm,len:longint;
function search(x,y:longint):longint;
var
  ll,rr,mid:longint;
begin
  if st[x]=x then exit(x);
    if st[y]<x then exit(y+1);
    ll:=x;rr:=y;
    while ll<=rr do begin
      mid:=(ll+rr) div 2;
        if st[mid]<x then ll:=mid+1 else rr:=mid-1;
    end;
    exit(ll);
end;
begin
  readln(n,m);
    for i:=1 to n do begin
        read(a[i]);
      st[i]:=max(st[i-1],last[a[i]]+1);
        q[i]:=i-st[i]+1;
      last[a[i]]:=i;
  end;
    for i:=1 to n do rmq[i,0]:=q[i];
    for j:=1 to trunc(ln(n)/ln(2)) do
      for i:=1 to n-(1 shl j)+1 do
            rmq[i,j]:=max(rmq[i,j-1],rmq[i+(1 shl (j-1)),j-1]);
    for i:=1 to m do begin
      readln(l,r);
        mm:=search(l,r);
        if mm>l then ans:=mm-l;
        if mm<=r then begin
          len:=trunc(ln(r-mm+1)/ln(2));
          ans:=max(ans,max(rmq[mm,len],rmq[r-(1 shl len)+1,len]));
        end;
        writeln(ans);
    end;
end.
相關文章
相關標籤/搜索