莫隊算法是一種用來處理區間問題的離線算法,也就是說,它會在把所有詢問讀進來之後,再利用詢問之間的關聯性來降低運算時間,所以它在像是 IOI 這種一定要先回傳答案才可以讀下一筆詢問的比賽不能用,但大多數的比賽都可以。
莫隊算法需要用到分塊法,令一塊大小為
要使用莫隊算法有一個先決條件:如果已經知道
接下來是莫隊算法的過程,首先,讀入每一筆詢問,然後對每一筆詢問依左界所在的塊由小到大排序,相同的話依右界位置(非塊編號)由小到大排序,接著算出第一筆詢問的答案後,用移動邊界的方式來算出下一筆詢問的答案。
pseudo code: 1
2
3
4
5
6
7
8
9
10
11
12
13array queries = 詢問們;
type ans; //目前答案
void add(type v){/*...*/} //增加一個數字,算新答案
void sub(type v){/*...*/} //移除一個數字,算新答案
int l = 0, r = -1;
for(Query q : queries){ //Query是一筆詢問的資料
while(r < q.右界) add(++r);
while(r > q.右界) sub(r--);
while(l < q.左界) sub(l++);
while(l > q.左界) add(--l);
q的答案 = ans;
}
只要把詢問的順序記下來,得出全部詢問的答案後依序輸出就好了。
再來是最重要的複雜度,乍看之下會覺得這樣一直加減加減的,一次還只移動一格,會不會太慢啊?
移動一次邊界算新答案是
左界在同一塊時,每次詢問左界最多移動
TODO