ST演算法詳解 例題 O(1)查詢區間最大最小值

2021-07-04 10:29:46 字數 4008 閱讀 2986

今天學習的是rmq的st演算法,先說明一下它的適用範圍

複雜度是o(nlogn)的建表和o(1)的查詢,並且不適用於更新。

如果更新的話就用線段樹了,因為st是直接建表了。

ps.(1 << k ) = 2^k

以最大值為例,最小值的初始化過程相同。

首先設a是要求區間最值的數列,f[i, j],表示從第i個數起連續 ( 1 << j )個數起連續數中的最大值。

比如來個數列:

1 2 3 5 7 9 2 10 3 4

f[1, 0]表示從第1個數起,長度為(1<<0) = 1位數範圍的最大值,為1;

f[1, 2]表示從第1個數起,長度為(1<<2) = 4位數範圍的最大值,為5;

f[2, 0]…為2;

f[2, 2]…為3;

可以發現,f[i, 0] = a[i]。

接下來是狀態轉移方程:

將f[i, j]分成兩段,從 i 到 i+(1<<(j - 1))-1為一段,i + (1<<(j-1))到 i + (1 << j) - 1為一段,求解這段的最大值就行了。

狀態轉移方程:

f[ i, j ] = max( f[ i ][ j - 1 ] , f[ i + (1<<(j - 1)) ] [j - 1] )

狀態轉移方程表示的意義:

數列a中,以第i個數開頭,總共(1 << j)位的連續子集的最大值,為以i開頭共(1<<(j-1))位的連續子集 和 以i+(1<<(j-1))開頭共(1<<(j-1))位的連續子集中的最大值。

初始化**:

void queryinit()

/////////

//////

//////

///////

for (int j = 1; (1

<< j) <= n; j++)

}}

假設要查詢lo到hi這一段區間的最大值,先需要求出乙個最大的k,使得k滿足:(1 << k ) <= (n - m + 1)。

這樣就可以把[lo, hi]這個區間分成兩個(有部分重疊)長度為(1 << k)位的兩個區間:[lo, lo + (1 << k)-1], [ hi - (1 << k)+1, hi]。

由於之前已經求出這倆部分的最大值,所以只需要o(1)的時間直接返回就行了。

**:

int querymin(int l, int r)

int querymax(int l, int r)

給n(10^5)個數組成單調不減數列,然後有q(10^5)個詢問。

每次詢問給乙個區間[fr,to],求區間內出現頻率最多的數字出現了幾次。

首先先構建被st演算法查詢的數列,由於是詢問區間內出現頻率最多的數字的個數,並且陣列單調不減,可以得知相同的數必定是連續出現的,所以可以直接統計乙個cnt的陣列,來記錄連續的值出現了幾次。

所以在查詢的時候,就要先把前一段的干擾剔除,然後再把剔除的干擾記回來,樣例的第一次詢問就能夠體現出來了。

#include 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define min(a,b) ((a)<(b)?(a):(b))

#define max(a,b) ((a)>(b)?(a):(b))

#define ll long long

#define lson lo, mi, rt << 1

#define rson mi + 1, hi, rt << 1 | 1

using namespace std;

const

int maxn = 100000 + 10;

const

int inf = 0x3f3f3f3f;

const

double eps = 1e-8;

const

double pi = acos(-1.0);

const

double ee = exp(1.0);

int n, q;

int num[maxn];

int cnt[maxn];

int minpoint[maxn][20];

int maxpoint[maxn][20];

void queryinit()

/////////

//////

//////

///////

for (int j = 1; (1

<< j) <= n; j++)

}}int querymin(int l, int r)

int querymax(int l, int r)

int main()

if (num[i] == num[i - 1])

else

}// for (int i = 1; i <= n; i++)

// cout << cnt[i] << " ";

// cout << endl << "-----------" << endl;

queryinit();

while (q--)

}return

0;}

給n個數,然後k個區間,求區間[lo, hi],之間最大值與最小值的差。

#include 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define min(a,b) ((a)<(b)?(a):(b))

#define max(a,b) ((a)>(b)?(a):(b))

#define ll long long

#define lson lo, mi, rt << 1

#define rson mi + 1, hi, rt << 1 | 1

using namespace std;

const

int maxn = 100000 + 10;

const

int inf = 0x3f3f3f3f;

const

double eps = 1e-8;

const

double pi = acos(-1.0);

const

double ee = exp(1.0);

int n, k;

int num[maxn];

int minpoint[maxn][20];

int maxpoint[maxn][20];

void queryinit()

/////////

//////

//////

///////

for (int j = 1; (1

<< j) <= n; j++)

}}int querymin(int l, int r)

int querymax(int l, int r)

int main()

queryinit();

while (k--)

}return

0;}

還有之前多校第一場那道也是可以用st演算法的。

早晨學st學到一半的時候停電了- -坑爹啊。

棧的最小值查詢(O1)

對棧增加乙個獲取最小值的方法 getmin 要求,時間複雜度為o 1 定義乙個minindex,儲存最小值的下標。每次push的時候比較大小,如果小於最小值,則minindex修改為當前下標,否則不修改。這樣貌似可以,但是發現乙個嚴重的問題,如果pop操作,把最小值出棧後,minindex就無效了。...

Linux程序排程之 大O 1 排程演算法

而大o 1 排程演算法其實就是一種非常好的排程演算法,它能讓程序排程的更加合理,更加快速。我將該排程演算法總結為下面這張圖 對其中一些內容做出解釋 1.首先將相同優先順序的程序放在同一佇列裡,然後有多少個優先順序,就建立多少個優先順序佇列,這些佇列可以用佇列陣列來表示。2.如果某一優先順序佇列裡沒有...

實現乙個 O 1 查詢的 LRU Cache

簡單來說 lru 是記憶體管理的一種演算法,淘汰最近不用的頁。o 1 時間完成查詢,那除了 hash 別無選擇。lru 用雙向鍊錶實現即可。資料結構選擇好了,查詢我們用 hash 通過 key 得到鍊錶節點的位置,然後更新 lru 鍊錶即可。簡單說下自己的專案,乙個類似 memcache 的小型資料...