MSP430G2ET時鐘系統
莫隊是莫濤大佬提出的演算法。
建議參閱:http://oi-wiki.com/misc/mo-algo/
0x00 前置知識
1 分塊(根號演算法)
2 sort
與運算子過載
0x01 演算法思想
例題:給你一個數列 \(a_i\), 對於每個\(l\)和\(r\) , 輸出\([l,r]\)中有多少種數 。
怎麼做呢?不會
演算法1:\(cnt_i\) 記錄 \(i\) 出現的次數,遍歷\(a_l\)到\(a_r\),\(++cnt_{a_i}\) ,最後掃描一遍,用\(ans\)計算多少個\(cnt\)不等於\(0\)。
這種做法,對於單個區間還挺好,對於多個區間就涼了(\(O(n^2)\))。
怎麼優化呢?
我們可以考慮利用之前的\(ans\)。
比如下面的栗子:
比如現在知道\([l_1,r_1]\)的結果,要求\([l_2,r_2]\)的結果。
現將\(l\)往右移一位……哦少了一個\(1\)!!!
\(--cnt_{1}\)
再比如將\(r\)向右移……多了一個\(1\)。
\(++cnt_{1}\)
於是:
演算法2:先得到一個區間的答案,再一步一步挪左右指標。
多好!
每次移動只有\(O(n)\)……欸那不還是\(O(n^2)\)?
但我就是覺得這演算法很好,所以要優化。
所以移動指標的次數要減小。
什麼好辦法呢?
分塊!
莫隊
我們考慮講區間分塊,然後對於每個區間,以區間右端為關鍵字由小至大排序。
這樣子對於每個塊內,右端點會一直往右移。複雜度是 \(O(n)\)。
然後如果從一個塊移到另一個塊,也是右端點一路向左退,也是 \(O(n)\) 的。
所以複雜度是 \(O(n \sqrt n)\)。
這時候有的小朋友就要問了:
「吶,為什麼不把它們都一起排序呢?✰」
問得好!
如果這樣,來康康我們的左端點君。
它有可能會從不停地從左往右移動,再從右往左……這樣複雜度退化成 \(O(n^2)\) 了。資料可以卡。
如果分塊的話,可以對整個數列適當地打亂,不容易被卡。
沒啦。
0x02 參考資料
https://www.cnblogs.com/WAMonster/p/10118934.html
http://oi-wiki.com/misc/mo-algo/