莫隊演算法 講解 更新

2022-05-05 21:21:09 字數 4955 閱讀 2225

莫隊演算法是用來處理一類無修改的離線區間詢問問題。——(摘自前國家隊隊長莫濤在知乎上對莫隊演算法的解釋。)

莫隊演算法是前國家隊隊長莫濤在比賽的時候想出來的演算法。

傳說中能解決一切區間處理問題的莫隊演算法。

準確的說,是離線區間問題。但是現在的莫隊被拓展到了有樹上莫隊,帶修莫隊(即帶修改的莫隊)。這裡只先講普通的莫隊。

還有一點,重要的事情說三遍!莫隊不是提莫隊長!莫隊不是提莫隊長!!莫隊不是提莫隊長!!!

看乙個例題:給定乙個n(n<50000)元素序列,有m(m<200000)個離線查詢。每次查詢乙個區間l~r,問每個元素出現次數為k的有幾個。(必須恰好是k,不能大於也不能小於)

這時候dalao們就直接樹狀陣列線段樹主席樹拍上去了,身為蒟蒻的我躲在角落瑟瑟發抖。該怎麼辦?

這時候就要使用莫隊演算法了。

接著上面的例題,直接暴力怎麼樣??

肯定會t的啊。

但是如果這個暴力我們給優化一下呢?

我們想,有兩個指標 curl 和 curr,curl 指向 l ,curr 指向 r。

l和r是乙個區間的左右兩端點。

利用 cnt 記錄每個數出現的次數,每次只是 cnt[a[curl]] cnt[a[curr]] 修改。

舉個栗子:

我們現在處理了curl—curr區間內的資料,現在左右移動,比如curl到curl-1,只需要更新上乙個新的3,即curl-1。

那麼curl到curl+1,我們只需要去除掉當前curl的值。因為curl+1是已經維護好了的。

curr同理,但是要注意方向哦!curr到curr+1是更新,curr到cur-1是去除。

我們先計算乙個區間[curl curr]的answer,這樣的話,我們就可以用o(1)轉移到[curl-1 curr] [curl+1 curr] [curl curr+1] [curl curr-1]上來並且求出這些區間的answer。

我們利用curl和curr,就可以移動到我們所需要求的[l r]上啦~

這樣做好像不會快很多,而且......

如果有個**資料,讓你在每個l和r間來回跑,而且跨度很大呢??

該\(t\)還是\(t\)...

怎麼辦?

但是這其實就是莫隊演算法的核心了。我們的莫隊演算法還有優化。

這就是莫隊演算法最精明的地方(我認為的qwq),也正是有了這個優化,莫隊演算法被稱為:優雅的暴力。

我們想,因為每次查詢是離線的,所以我們先給每次的查詢排乙個序。

一種直觀的辦法是按照左端點排序,再按照右端點排序。但是這樣的表現不好。特別是面對精心設計的資料,這樣方法表現得很差。

舉個栗子,有6個詢問如下:(1, 100), (2, 2), (3, 99), (4, 4), (5, 102), (6, 7)。

這個資料已經按照左端點排序了。用上述方法處理時,左端點會移動6次,右端點會移動移動98+97+95+98+95=483次。

其實我們稍微改變一下詢問處理的順序就能做得更好:(2, 2), (4, 4), (6, 7), (5, 102), (3, 99), (1, 100)。

左端點移動次數為2+2+1+2+2=9次,比原來稍多。右端點移動次數為2+3+95+3+1=104,右端點的移動次數大大降低了。

上面的過程啟發我們:

我們不應該嚴格按照公升序排序,而是根據需要靈活一點的排序方法

那麼排序的方法是

我們把所有的元素分成多個塊。再按照左端點塊編號從小到大排序,左端點塊編號相同按右端點塊編號從小到大。

這樣對於不同的查詢

例如:我們有長度為9的序列。

1 2 3 4 5 6 7 8 9 分為1——3 4——6 7——9

查詢有7組。[1 2] [2 9] [1 3] [6 9] [5 8] [3 8] [8 9]

排序後就是:[1 2] [1 3] [3 8] [2 9] | [5 8] [6 9] | [8 9]

然後我們按照這個順序移動指標就好啦~

時間複雜度證明

其實從不同的角度看,證法很多:對於左端點在乙個塊中時,右端點最壞情況是從盡量左到盡量右,所以右端點跳時間複雜度o(n),左端點一共可以在n0.5個塊中,所以總時間複雜度o(n*n0.5) = o(n^1.5)。

1.對於每組查詢的記錄和排序:

l,r為左右區間編號,p是第幾組查詢的編號

struct querye[maxn];

bool cmp(query a, query b)

2.處理和初始變數:

answer就是所求答案,bl是分塊數量,a是原序列,ans是記錄原查詢序列下的答案,cnt是記錄對於每個數i,cnt[i]表示i出現過的次數,curl和curr不再解釋,nmk題意要求。

int answer, a[maxn], m, n, bl, ans[maxn], cnt[maxn], k, curl = 1, curr = 0;

void add(int pos)//新增

void remove(int pos)//去除

//一般寫法都是邊處理 邊根據處理求答案。cnt[a[pos]]就是在pos位置上原序列a出現的次數。

3.主體部分及輸出:

預處理查詢編號,用四個while移動指標順便處理。

n = read(); m = read(); k = read();

bl = sqrt(n);

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

a[i] = read();

for(int i = 1; i <= m; i++)

sort(e+1,e+1+m,cmp);

for(int i = 1; i <= m; i++)

for(int i = 1; i <= m; i++)

printf("%d\n",ans[i]);

return 0;

在這裡著重說下四個while

當curl < l 時,我們當前curl是已經處理好的了。所以remove時先去除當前curl再++

當curl > l 時,我們當前curl是已經處理好的了。所以 add 時先--再加上改後curl

當curr > r 時,我們當前curr是已經處理好的了。所以remove時先去除當前curr再--

當curr < r 時,我們當前curr是已經處理好的了。所以 add 時先++再加上改後curr

【luogu p2709 小b的詢問】

\(add\)和\(remove\)對於平方相加減的運算利用完全平方式逆回去。

1^2 = 1;

2^2 = (1+1)^2 = 1 + 1*2 + 1;

3^2 = (1+2)^2 = 1 + 2*2 + 4;

4^2 = (1+3)^2 = 1 + 3*2 + 9;

//小b的詢問 

#include #include #include #include using namespace std;

const int maxn = 50001;

int answer, a[maxn], m, n, bl, ans[maxn], cnt[maxn], k, curl = 1, curr = 0;

void add(int pos)

void remove(int pos)

inline int read()

return k;

}struct querye[maxn];

bool cmp(query a, query b)

int main()

sort(e+1,e+1+m,cmp);

for(int i = 1; i <= m; i++)

for(int i = 1; i <= m; i++)

printf("%d\n",ans[i]);

return 0;

}

【luogu p4462 [cqoi2018]異或序列】

ax+ax-1+...+ay = cntx+cnty 這樣把一段序列變成兩段相加跑莫隊。

#include #include #include #include using namespace std;

const int maxn = 200010;

int curr = 0, curl = 1, answer,a[maxn], ans[maxn], cnt[maxn], n, m, k, bl;

struct queryq[maxn];

bool cmp(const query &a, const query &b)

for(int i = 1; i <= m; i++)

printf("%d\n",ans[i]);

return 0;

}

莫隊演算法適用條件是比較苛刻的嗎?是的。

①題目必須離線

②能夠以極少的時間推出旁邊區間(一般是o(1))

③沒有修改或者修改不太苛刻

④基於分塊,分塊不行,它也好不了**去

但莫隊的思想美妙,**優美,你值得擁有。莫隊的排序思想也為眾多離線處理的題目提供了完整的思路。

我們可以看出來,對於莫隊這種模擬式的暴力演算法很好理解。

也比較實用,亂搞神器不是浪得虛名。

莫隊是我自學的,所以在文章裡或許會有些個人理解上的偏差,還請各位dalao能賜教。自學能力是需要培養、鍛鍊的。

qvqqaq

qnq祝各位oi路途能越走越順!

本蒟蒻\(qq\) 935145183/3203600070

蒟蒻位址

莫隊演算法講解

問題 有n個數組成乙個序列,有m個形如詢問l,r的詢問,每次詢問需要回答區間內至少出現2次的數有哪些。樸素的解法需要讀取o nm 次數。如果資料範圍小,可以用陣列,時間複雜度為o nm 如果使用stl的map來儲存出現的次數,則需要o nmlogn 的複雜度。有沒有更快的方法呢?得出。如果能安排適當...

莫隊講解 普通莫隊

結束了分塊,我們來講下莫隊。據我所知,莫隊能解決一切區間問題,除了翻轉。因為它就是個暴力 其實這兩者的關係並不大。僅僅是時間複雜度一樣而已。我們把原序列分成 n塊 好像就是這裡相同 這裡說的序列是查詢序列l r,並不是讀入的a i 之後我們把序列排序 按照第一關鍵字為左端點所在的塊的大小,如果相同就...

HDU4638 莫隊演算法,講解)

解題思路 第一次做莫隊演算法,推薦部落格入門。結合上面那片部落格,我先講一下自己對莫隊演算法時間複雜度的理解。首先我們將乙個block設為sqrt n n為數列的長度。然後按提的問題的區間進行排序,排序的規則是 這麼做的目的是使l和r指標的移動複雜度盡可能相等。1.首先對l的移動複雜度進行分析。l最...