---
# System prepended metadata

title: 莫隊(分塊算法)
tags: [algorithm]

---

---
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}})$

其實這樣的複雜度是最壞的情況下會跑到的，所以實際上的複雜度會更好。

## 結語
莫隊是一個十分優美的演算法，而且複雜度也足夠使用，程式碼也十分簡短，學起來的話在解題目時也會有別的想法。