今天學習的是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 4f[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))位的連續子集中的最大值。
初始化**:
假設要查詢lo到hi這一段區間的最大值,先需要求出乙個最大的k,使得k滿足:(1 << k ) <= (n - m + 1)。void queryinit()
/////////
//////
//////
///////
for (int j = 1; (1
<< j) <= n; j++)
}}
這樣就可以把[lo, hi]這個區間分成兩個(有部分重疊)長度為(1 << k)位的兩個區間:[lo, lo + (1 << k)-1], [ hi - (1 << k)+1, hi]。
由於之前已經求出這倆部分的最大值,所以只需要o(1)的時間直接返回就行了。
**:
給n(10^5)個數組成單調不減數列,然後有q(10^5)個詢問。int querymin(int l, int r)
int querymax(int l, int r)
每次詢問給乙個區間[fr,to],求區間內出現頻率最多的數字出現了幾次。
首先先構建被st演算法查詢的數列,由於是詢問區間內出現頻率最多的數字的個數,並且陣列單調不減,可以得知相同的數必定是連續出現的,所以可以直接統計乙個cnt的陣列,來記錄連續的值出現了幾次。
所以在查詢的時候,就要先把前一段的干擾剔除,然後再把剔除的干擾記回來,樣例的第一次詢問就能夠體現出來了。
給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, 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;}
還有之前多校第一場那道也是可以用st演算法的。#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學到一半的時候停電了- -坑爹啊。
棧的最小值查詢(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 的小型資料...