---
tags: algorithm
---
# 莫隊(分塊算法)
若序列長度為$N$且共有$Q$筆區間詢問。
且區間之間可以正確的轉移
藉由對區間詢問進行**特別的排序**來達到一個很不錯的複雜度
## 一般莫隊
對於每一個詢問需從目前的區間$[l, r]$跑到詢問的節點$[ql, qr]$
而且從區間$[l, r]$到$[l, r + 1], [l, r - 1], [l + 1, r], [l - 1, r]$皆有$O(1)$的複雜度
可以發現,如果只是單純的進行轉移的話,對於每一筆詢問$l, r 最多皆會跑到O(N),所以時間複雜度為O(N^2)$
那我們要怎們樣可以降低時間複雜度呢$\implies$ **分塊**
如果把$l$以$\sqrt{N}$為一塊,然後對於每一塊內對$r$進行排序
則複雜度為$O(N\sqrt{N})$
```cpp=
#include <bits/stdc++.h>
using namespace std;
#define F first
#define S second
int sqt;
bool cmp(pair<int,int> a ,pair<int,int> b) {
return (a.F / sqt == b.F / sqt ? a.S < b.S : a.F < b.F);
}
int main () {
int N; cin >> N;
sqt = int(sqrt(N)) + 1;
vector<pair<int,int>> query(N);
sort(query.begin(), query.end(), cmp);
}
```
> 對於每一塊來說,$r$最多會從$1$跑到$N$,共有$\sqrt{N}$塊 $\implies O(N\sqrt{N})$
>在每一塊內$l$最多會跑$\sqrt{N}$次,共有$Q$筆詢問 $\implies Q\sqrt{N}$
> 總時間複雜度$O((Q + N) \sqrt{N})$
## 觀察
可以觀察到,只要從$[l, r]$轉移到$[l, r + 1], [l, r - 1], [l + 1, r], [l - 1, r]的複雜度為T,則複雜度會變為O(T\times N\sqrt{N})$
## 帶修改莫隊
只是從二維變成了三維
不過分塊的地方就不太一樣了
可以嘗試自己證證看,不過先說結論,對於$t$跟$l$來說,以$\displaystyle n^{\frac{2}{3}}$為一塊,這樣複雜度會是$O(N^{\frac{5}{3}})$
若序列長度為$N$
對於$t, l$分別以$a_1, a_2$為一塊進行排序,則複雜度為
$\displaystyle \implies a_1N + \frac{N}{a_1} (a_2N + N\times\frac{N}{a_2})$
> $t$每次最多跑$a_1$次,而對於接下來的$l, r$更新來說,最多會跑$\displaystyle \frac{N}{a_1}$次,然後就跟一般莫隊一樣,會跑$\displaystyle a_2N + N\times\frac{N}{a_2}$次
> $N \times (\displaystyle a_1 + \frac{a_2N}{a_1} + \frac{N^2}{a_1a_2}),當a_1 = a_2 = n^{\frac{2}{3}}$時複雜度會有最小值$O(N^{\frac{5}{3}})$
其實這樣的複雜度是最壞的情況下會跑到的,所以實際上的複雜度會更好。
## 結語
莫隊是一個十分優美的演算法,而且複雜度也足夠使用,程式碼也十分簡短,學起來的話在解題目時也會有別的想法。